Sashité for Developers
  1. Sashité for Developers
  2. Nostr
  3. NIPs
  4. Direct Challenge

NIP-XX

Direct Challenge

draft optional

This NIP defines a Nostr event kind for publicly proposing a session in a turn-based, two-player abstract strategy board game of the chess family. The Direct Challenge event names a specific opponent, names the rule system the session is played under, designates the session’s timing authority, and carries the proposed session terms. The same event, carrying two additional references, proposes the rematch of a concluded session.

Abstract

Defines kind: 3420, a regular event by which a player publicly proposes a session to a specific opponent. The event identifies the challenged player, designates the timing authority (an optional timestamper, or one or more timing relays), and carries the game context (shared), the rule system (shared), an optional proposed variant for each player, a time-control configuration, an optional seat assignment, and an acceptance deadline.

The challenged player accepts by publishing the session’s Game Session (kind 3422) referencing the challenge: acceptance and founding are one act, and the session is playable from that event. A challenge that receives no Game Session before its deadline expires without effect.

A Direct Challenge carrying a rematch_of reference proposes a rematch of a concluded session between its two players, with the seats swapped and every other term reused (§Rematch challenge).

Motivation

Initiating a session on Nostr requires a public, signed proposal that fixes session terms cryptographically. A Direct Challenge expresses one player’s offer to play another specific player under specific terms, witnessed by relays. The challenged player either founds the session (Game Session, kind 3422) or remains silent (letting the challenge expire). No “decline” event is defined; silence is the refusal.

This NIP carries only the structural and lifecycle aspects of the challenge. Game-specific semantics — what a given variant identifier means within a given game context, what the piece movements are, how time accounting elapses, how the session concludes — are determined by the rule-system document the challenge names (§Match-terms tags, the rules term). The suite designates no arbiter: every rule question of the session is a function of its public events and of that document, so any consumer can compute the same answer, and the players themselves found and conclude the session (kind 3422; kind 3425).

A Direct Challenge MAY leave either player’s variant — the challenged player’s, the challenger’s own, or both — or the seat assignment unfixed. In that case, the challenged player supplies the missing piece(s) in the Game Session. Omitting one’s own variant is a deliberate delegation: the challenger consents, by omission, to the challenged player choosing the variant the challenger will play.

Specification

Event kind

kind: 3420

Direct Challenge events are regular events per NIP-01: immutable once signed and broadcast.

Timing designation

This NIP requires exactly one timing designation, selecting the session’s timing mode per the Canonical Timing NIP §Timing modes and mode selection:

The designation fixes the timing mode (Canonical Timing §Canonical timing of an event):

The self-timed guarantee is scoped to the designated relay(s): canonical timing is established for consumers who retrieve the session’s events from a designated relay (or a mirror they trust to be faithful to one), because acceptance itself is the timing bound. It leaves no in-event trace — an event re-encountered elsewhere, on an arbitrary relay or in an export, carries only its self-declared created_at, and nothing distinguishes an honestly-timed event from a backdated one accepted by a lax relay. A consumer who cannot establish acceptance by a designated relay MUST treat the event as pending (Canonical Timing §The pending state); the designation is what tells every verifier, forever, which relays to ask.

Designating a timestamper trades the self-timed baseline for signed, portable, third-party-verifiable timing — closing exactly the late-verifier gap above; whether a deployment offers it is a deployment choice, outside this generic NIP.

In attested mode, the timestamper publishes Event Timestamp Attestations (kind 3410, see the Event Timestamp Attestation NIP) for the session’s attested events: on this directed path, the Game Session (kind 3422), each Ply (kind 3423) and each Conclusion (kind 3425) — and, on the matchmaking path, the Pairing (kind 3419) before the Game Session. The Direct Challenge itself (kind 3420) is NOT attested. Attestations from any signer other than the designated timestamper are ignored. In self-timed mode there are no attestations; the same events acquire canonical timing from their own created_at once a designated timing relay accepts them. Kinds 3426 (Elo Rating Attestation) and 3427 (Glicko-2 Rating Attestation) are generic, suite-independent NIPs signed by a rating authority outside the session’s timing mode. The canonical timing is the authoritative source for race resolution and clock accounting; see Canonical Timing.

The timing designation propagates to the Game Session, which MUST mirror it exactly (the same timestamper, or the same timing_relay set).

Tags

Player tags

Tag Cardinality Value Description
p one or two pubkey, optional relay hint, role marker exactly one opponent (the challenged player); and, in attested mode, exactly one timestamper
timing_relay zero or more relay URL (wss://…) in self-timed mode (the default), one or more designated timing relays; absent in attested mode

The p tags MUST carry a role marker as the fourth element:

The p tags serve a dual purpose: semantic (identifying the participants) and structural (notification routing via standard Nostr #p filters). The dual purpose is intentional — repeating identifiable pubkeys in p tags enables compatibility with the standard Nostr discovery mechanism without requiring chained subscription logic on the client side.

The relay hint (third element of a p tag) is OPTIONAL but SHOULD be present. When omitted, an empty string MAY be used in its position.

Constraints on the pubkeys:

Match-terms tags

Tag Cardinality Value Description
game exactly one game identifier the broader game context shared by both players
variant zero to two pubkey + variant identifier variant assigned to a player
time_control one or more duration + optional increment + optional plies session time-accounting period
seat zero or one first or second seat declared by the challenger (the challenged player implicitly receives the other value)
rules exactly one digest + optional retrieval hint the rule-system document the session is played under

At least one time_control tag MUST be present. Multiple time_control tags MAY be present to encode multi-period time controls (e.g., classical chess time controls with extra time after a given move count).

A game identifier and a variant identifier are each a non-empty ASCII string starting with a lowercase letter, followed by zero or more lowercase letters or digits, with total length between 1 and 32 characters inclusive. Each MUST match the regular expression:

^[a-z][a-z0-9]{0,31}$

This NIP does not prescribe a vocabulary for these identifiers. The interpretation of a given game or variant identifier is the responsibility of the consuming application. Conceptually:

The relationship between game and variant is application-defined. A given application MAY (and typically does) constrain which variants are valid within each game it supports, including constraints such as a shared board dimension. The protocol itself does not validate this relationship; it is the responsibility of the consuming application.

The game tag is structured ["game", "<game_id>"] and applies to the entire session (singleton).

Each variant tag is structured ["variant", "<pubkey>", "<variant_id>"]:

Each player’s variant is OPTIONAL in the Direct Challenge — the challenger’s own (the variant tag whose second element matches the event’s pubkey) as well as the challenged player’s. Any variant the Direct Challenge does not fix is supplied by the challenged player in the Game Session (kind 3422 §Variants): the (Direct Challenge, Game Session) pair always jointly fixes both variants. A challenger who omits their own variant thereby delegates its choice to the challenged player; a challenger who omits the challenged player’s variant leaves that player free to choose their own, as before.

When both players’ variants are specified and they differ, the session is a multi-variant session. Multi-variant sessions are valid at the protocol level; whether a rating filter binds across them depends on the declared pool scope (kind 3418 §Match-terms tags): excluded under a pervariant scope, rated together under a pergame scope that unifies the game’s variants.

A time_control tag describes one period of session-wide time accounting:

["time_control", "<duration>", "<increment>", "<plies>"]

Element Position Type Required Description
duration 2nd non-negative integer (seconds) yes Time budget for the period
increment 3rd non-negative integer (seconds) no Fischer increment per ply within this period
plies 4th strictly positive integer no Move-count quota — period covers N plies, then repeats

Trailing optional fields MAY be omitted. If increment is omitted, plies MUST also be omitted. Each present element is a bare decimal integer matching ^(0|[1-9][0-9]*)$no leading zeros — and an empty-string element is malformed: omit the element rather than leaving it empty. A duration of 0 is valid only in the three-element per-move form (duration: 0 + increment + plies, the last row below); in any other form the duration MUST be strictly positive. An omitted increment and an explicit increment of 0 are distinct configurations: no normalization is applied anywhere in the suite, so ["time_control", "300"] and ["time_control", "300", "0"] are different time controls — and with leading zeros excluded, comparing configurations for identity (kind 3419 §Match-terms tags; §Rematch challenge) reduces to element-wise string equality.

The semantics:

Configuration Semantic
duration only Fixed time bank; duration depletes linearly per ply.
duration + increment Fischer time control: increment is added to the budget after each ply.
duration + increment + plies Quota-based time control (byōyomi, Canadian): the period covers plies plies, then resets.
duration: 0 + increment + plies Per-move time accounting with no main clock.

When multiple time_control tags are present, they are applied sequentially in tag order (e.g., main time bank followed by overtime periods). The precise per-ply accounting — spend rules, increment timing, period transitions, and timeout detection — is rule-system-defined; the rule-system document named by rules specifies it (for the reference sanki rule system: Time Accounting — Sanki, incorporated in the kernel the manifest names).

A seat tag declares the seat the signer (the challenger) claims in the proposed session:

["seat", "<seat_name>"]

If the seat tag is absent, the seat assignment is unfixed and supplied by the challenged player in the Game Session.

A rules tag names the rule system the two players commit to for the session:

["rules", "<digest>", "<hint>"]

The format of the rule-system document is defined by the consuming application for each game — this NIP does not prescribe it, exactly as it does not prescribe the encodings of positions and moves. For the reference sanki rule system, the document is the manifest specified in Rule System — Sanki (in this suite’s application layer): the identifier and digests of the fixed rule kernel it targets, the pseudo-legal movement tables (GGN) of each variant, and the parameters of the game’s terminations, captures and promotions. The document MUST prescribe a deterministic initial position for the session’s variants: the suite designates no party to draw a randomized setup.

rules completes game and variant: game names the family, variant selects each player’s piece set and movement within the document, rules says which document. Every consumer of the session — the players’ clients, observers, rating authorities — applies exactly this document and nothing else, and a session can be replayed from public events alone to the same verdict by anyone holding the document: this is what allows the suite to do without an arbiter, the founding (kind 3422) and the conclusion (kind 3425) being signed by the players and verified, not believed. A rule-system document is frozen for the life of the session: a revised document (a rebalancing, a fix) is a new digest, which applies only to sessions founded under it.

Lifecycle tags

Tag Cardinality Value Description
accept_until exactly one unix timestamp (seconds) the latest moment at which a Game Session founded on this challenge is valid
nonce exactly one nonce + difficulty NIP-13 proof of work

The accept_until value (second element) MUST be a Unix timestamp in seconds, strictly greater than the event’s created_at. A Game Session whose canonical timing is greater than this value MUST be treated as invalid by verifiers; see kind 3422 §Semantic constraints (constraint 9) and Canonical Timing. A NIP-40 expiration tag MUST NOT be carried: NIP-40 authorizes relays to delete the event, and a consumed Direct Challenge must remain retrievable indefinitely — the Game Session’s founding reference is verified against it (kind 3422 §Semantic constraints), so garbage-collecting an expired-but-accepted challenge would amputate the founding evidence of a live session.

The nonce tag follows NIP-13.

Rematch tags

Tag Cardinality Value Description
e rematch_of zero or one event id, optional relay hint, marker rematch_of the concluded Game Session (kind 3422) whose rematch is proposed
e concluded_by zero or one event id, optional relay hint, marker concluded_by a Conclusion (kind 3425) of that same session — the proof that it is over

A Direct Challenge carries either both rematch tags or neither. When both are present the event is a rematch challenge and §Rematch challenge applies; otherwise it carries no e tag at all (a fresh challenge references no prior suite event).

Rematch challenge

A rematch challenge proposes to replay a concluded session between the same two players, with the seats swapped and every other term reused. It is an ordinary Direct Challenge — accepted the same way, by the Game Session (kind 3422) the challenged player publishes — whose terms are constrained rather than chosen:

Nothing is left open: a rematch challenge fixes both variants and the seat, so the Game Session founded on it supplies no term of its own. Its accept_until is the offerer’s choice, as for any challenge; its content is free.

One rematch per concluded session. The two players MAY each publish a rematch challenge of the same session, in either order or simultaneously; each is acceptable by its addressee. Whichever Game Session is founded first canonically occupies the slot (rematch, concluded session) (kind 3422 §Idempotence and race resolution), and every other rematch challenge of that session — accepted or not — is moot: one concluded session founds at most one rematch, however many challenges its players publish. A rematch of the rematch is proposed, in turn, against the new concluded session.

Content

The content field MAY carry a free-form text message from the challenger to the challenged player (e.g., a greeting, context, or terms clarification). It is OPTIONAL: an empty string is permitted.

The content field MUST satisfy:

The content field MUST NOT alter the interpretation of the challenge. The challenged player’s client MAY display it to the user, but MUST NOT base any normative decision (acceptance, seat selection, terms negotiation) on its value. The semantic meaning of the event is fully carried by the tags.

The protocol intentionally does NOT restrict characters such as <, >, &, ", ', `, or \ — these are legitimate text in many natural contexts (mathematics, programming notation, casual messaging across multiple scripts); handling them is the job of output-layer escaping in the client (see §Security considerations).

Semantic constraints

A conforming Direct Challenge event MUST satisfy all of the following:

  1. The event contains exactly one opponent-marked p tag and exactly one timing designation: either exactly one timestamper-marked p tag and no timing_relay tag (attested mode), or no timestamper and one or more timing_relay tags each carrying a wss:// URL (self-timed mode, the default) — per the Canonical Timing NIP §Timing modes and mode selection.
  2. The pubkeys of all p tags MUST be distinct from the event’s pubkey (the challenger). The opponent pubkey MUST be distinct from the timestamper pubkey (attested mode).
  3. Exactly one game tag is present, with a valid game identifier matching ^[a-z][a-z0-9]{0,31}$.
  4. Zero, one, or two variant tags are present. Each variant’s third element is a valid variant identifier matching ^[a-z][a-z0-9]{0,31}$.
  5. Each variant tag’s second element matches either the challenger’s pubkey or the challenged player’s pubkey. At most one variant tag is present per pubkey.
  6. At least one time_control tag is present. Each time_control tag has a valid format (per §Match-terms tags: no leading zeros; duration 0 only in the three-element per-move form).
  7. If a seat tag is present, its second element is first or second.
  8. Exactly one accept_until tag is present, with a value strictly greater than created_at.
  9. Exactly one nonce tag is present, carries its committed difficulty as its third element, and the event id satisfies NIP-13 against that committed difficulty — never against a relay’s advertised minimum (an ingestion policy, not conformance).
  10. Exactly one rules tag is present, whose second element matches ^[0-9a-f]{64}$ (per §Match-terms tags).
  11. The event carries either no e tag, or exactly one rematch_of-marked and exactly one concluded_by-marked e tag and no other. In the latter case (a rematch challenge) the event satisfies §Rematch challenge: the signer and the opponent are the two players of the referenced concluded Game Session; concluded_by references a conforming Conclusion of that session; game, rules, the time_control sequence, both variant tags and the timing designation equal the concluded Game Session’s; and the seat tag is present and is the other value than the signer’s seat there.

Constraints 1–10 are checkable from the event alone; constraint 11 requires cross-event validation against the referenced Game Session and Conclusion. Whether the rule-system document named by rules is retrievable is not a conformance matter (§Validation responsibility).

Validation responsibility

Validation of a Direct Challenge against the semantic constraints (§Semantic constraints) is the responsibility of the client and the relay. A relay additionally enforces its advertised NIP-13 minimum difficulty at ingestion — a relay policy, distinct from conformance.

Validation of the proposed terms in the broader sense (e.g., whether the rule-system document named by rules is retrievable and implementable by the client, whether it defines the proposed game / variant combination, or whether the time-control configuration is one the client can account for) is the responsibility of the challenged player’s client when deciding whether to found the session. A client MUST NOT publish a Game Session for a challenge whose rule-system document it cannot retrieve and execute: it would be committing to a session it cannot play or conclude.

Separately, a deployment MAY gate the eligibility to impose certain optional terms behind its own policy — independent of format validity, which stays as above. Such gates are deployment policy layered on top of this generic NIP, not part of it.

Examples

The examples below use placeholder pubkeys, relay URLs, and timestamps for illustration.

Example 1 — All terms fixed by the challenger

Alice proposes a session to Bob in attested mode, designating Dave as timestamper. The game context is chess (a standalone chess application), and both players play the standard chess variant. Alice plays seat first with a 5-minute Fischer time control with 3-second increment.

{
 "kind": 3420,
 "pubkey": "<alice_pubkey>",
 "created_at": 1700000000,
 "tags": [
 ["p", "<bob_pubkey>", "wss://relay.example.com", "opponent"],
 ["p", "<dave_pubkey>", "wss://relay.example.com", "timestamper"],
 ["game", "chess"],
 ["rules", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com"],
 ["variant", "<alice_pubkey>", "chess"],
 ["variant", "<bob_pubkey>", "chess"],
 ["seat", "first"],
 ["time_control", "300", "3"],
 ["accept_until", "1700003600"],
 ["nonce", "62473", "16"]
 ],
 "content": "Up for a game?",
 "id": "...",
 "sig": "..."
}

Bob accepts by publishing the Game Session (kind 3422) referencing this event; its canonical timing must not exceed 1700003600.

Example 2 — Terms partially deferred

Alice proposes a session against Bob in the chess game context. She fixes her own variant (chess) but leaves Bob’s variant and the seat assignment open for Bob to decide in the Game Session.

{
 "kind": 3420,
 "pubkey": "<alice_pubkey>",
 "created_at": 1700000000,
 "tags": [
 ["p", "<bob_pubkey>", "wss://relay.example.com", "opponent"],
 ["p", "<dave_pubkey>", "wss://relay.example.com", "timestamper"],
 ["game", "chess"],
 ["rules", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com"],
 ["variant", "<alice_pubkey>", "chess"],
 ["time_control", "600"],
 ["accept_until", "1700003600"],
 ["nonce", "11337", "16"]
 ],
 "content": "",
 "id": "...",
 "sig": "..."
}

Example 3 — Fully deferred variants

Bob challenges Alice in the sanki game context without fixing any variant: he delegates the choice of his own variant to Alice, and leaves Alice free to choose hers. Alice supplies both in the Game Session (kind 3422), explicitly or via her client’s deterministic default.

{
 "kind": 3420,
 "pubkey": "<bob_pubkey>",
 "created_at": 1700000000,
 "tags": [
 ["p", "<alice_pubkey>", "wss://relay.example.com", "opponent"],
 ["timing_relay", "wss://relay.example.com"],
 ["game", "sanki"],
 ["rules", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com"],
 ["time_control", "300", "3"],
 ["accept_until", "1700003600"],
 ["nonce", "27193", "16"]
 ],
 "content": "Your pick — both sides.",
 "id": "...",
 "sig": "..."
}

No eligibility gate is involved: Bob imposes nothing on Alice, and Alice completing Bob’s variant in the Game Session is a delegation Bob consented to by omission (see §Validation responsibility).

Example 4 — Self-timed session (no timestamper)

Alice proposes a sanki session to Bob in self-timed mode: no timestamper is designated, and a timing_relay tag names the relay whose acceptance will establish the session events’ canonical timing — its strict ingestion window (it refuses any event whose created_at is in the past relative to its clock, beyond a small past tolerance) makes each accepted event’s own created_at trustworthy.

{
 "kind": 3420,
 "pubkey": "<alice_pubkey>",
 "created_at": 1700000000,
 "tags": [
 ["p", "<bob_pubkey>", "wss://relay.example.com", "opponent"],
 ["timing_relay", "wss://relay.example.com"],
 ["game", "sanki"],
 ["rules", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com"],
 ["variant", "<alice_pubkey>", "ogi"],
 ["variant", "<bob_pubkey>", "chess"],
 ["seat", "first"],
 ["time_control", "300", "3"],
 ["accept_until", "1700003600"],
 ["nonce", "62473", "16"]
 ],
 "content": "Up for a game of sanki?",
 "id": "...",
 "sig": "..."
}

Example 5 — Sashité Sanki session with multi-period time control

Alice proposes a Sanki session against Bob: the game is sanki (a multi-game 8x8 platform), and both players choose the ogi variant (an 8x8 adaptation of shōgi). 60 minutes main time, followed by 30 seconds per ply overtime.

{
 "kind": 3420,
 "pubkey": "<alice_pubkey>",
 "created_at": 1700000000,
 "tags": [
 ["p", "<bob_pubkey>", "wss://relay.example.com", "opponent"],
 ["p", "<dave_pubkey>", "wss://relay.example.com", "timestamper"],
 ["game", "sanki"],
 ["rules", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com"],
 ["variant", "<alice_pubkey>", "ogi"],
 ["variant", "<bob_pubkey>", "ogi"],
 ["time_control", "3600"],
 ["time_control", "0", "30", "1"],
 ["accept_until", "1700003600"],
 ["nonce", "84217", "16"]
 ],
 "content": "",
 "id": "...",
 "sig": "..."
}

The two time_control tags express sequential periods: first a 60-minute fixed bank, then per-ply overtime.

Example 6 — Rematch challenge

Alice and Bob’s self-timed sanki session (Game Session <session_id>, Alice first playing ogi, Bob second playing chess, 5+3) has concluded — Bob published its Conclusion (<conclusion_id>). Alice proposes the rematch: same terms, and she now takes seat second. Bob accepts by publishing the new Game Session (kind 3422), which occupies the slot (rematch, <session_id>).

{
 "kind": 3420,
 "pubkey": "<alice_pubkey>",
 "created_at": 1700009000,
 "tags": [
 ["e", "<session_id>", "wss://relay.example.com", "rematch_of"],
 ["e", "<conclusion_id>", "wss://relay.example.com", "concluded_by"],
 ["p", "<bob_pubkey>", "wss://relay.example.com", "opponent"],
 ["timing_relay", "wss://relay.example.com"],
 ["game", "sanki"],
 ["rules", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com"],
 ["variant", "<alice_pubkey>", "ogi"],
 ["variant", "<bob_pubkey>", "chess"],
 ["seat", "second"],
 ["time_control", "300", "3"],
 ["accept_until", "1700009600"],
 ["nonce", "40961", "16"]
 ],
 "content": "Again?",
 "id": "...",
 "sig": "..."
}

Had Bob published his own rematch challenge at the same time (seat first, addressed to Alice), either Game Session founded on either challenge would do; the first canonical one wins the slot and the other challenge is moot.

Client guidelines

Clients implementing kind: 3420 SHOULD:

  1. Subscribe to kind 3420 with a #p filter on the user’s pubkey to receive incoming challenges.
  2. Inspect the role marker (fourth element of each p tag) to identify whether the user is the opponent of a given challenge.
  3. Surface to the user the session’s timing designation — the designated timestamper’s identity (attested mode) or the designated timing relay URLs (self-timed mode) — since it provides authoritative timing for both clock accounting and race resolution. The user SHOULD assess whether they trust the designated parties before accepting.
  4. Validate received Direct Challenge events against the semantic constraints (§Semantic constraints) before presenting them to the user.
  5. Before offering to accept, retrieve the rule-system document named by rules, verify its digest, and check that the client implements the kernel it names; a challenge whose document the client cannot execute SHOULD be presented as unplayable, never accepted.
  6. Filter out challenges whose accept_until has already elapsed.
  7. Allow users to dismiss unwanted challenges locally without publishing any event.
  8. Respect the user’s NIP-51 mute list (kind 10000): Direct Challenges signed by a pubkey listed in the user’s mute list SHOULD be silently filtered out from display, unless the user explicitly opts in to view challenges from muted senders.
  9. Before sending a Direct Challenge, fetch the recipient’s Challenge Policy (kind 30420) for the proposed game and apply the lookup rules defined in that NIP’s §Mode lookup (the policy is per-game and variant-agnostic). If the policy is likely to filter the challenge (e.g., nobody mode, following mode without the sender being followed, rating mode with the sender outside the rating threshold — or with no determinable comparison pool), warn the sender before publishing — the challenge would consume proof of work without ever being surfaced to the recipient.
  10. When receiving a Direct Challenge, apply the user’s Challenge Policy (kind 30420) filter for the proposed game (per that NIP’s §Mode lookup), after the NIP-51 mute list filter. Challenges that do not satisfy the policy SHOULD be silently filtered out from display, unless the user explicitly opts in.
  11. When displaying a Direct Challenge to the user, attempt to verify the NIP-05 identifier of the challenger (event signer). Display verified NIP-05 identifiers prominently. When verification fails (network error, DNS issue, or no identifier published), display the pubkey directly and indicate the identity as unverified — but do not block the challenge solely on this basis.
  12. Present a rematch challenge as such — “rematch of your game against …” — and offer it only against a session whose Conclusion the client has itself observed, since the challenge’s proof of conclusion is checkable.

Clients MAY:

Security considerations

NIP-13 proof of work and NIP-51 mute lists are complementary: PoW raises the per-event cost of bulk spam; mute lists eliminate persistent senders entirely.

The protocol-level character restrictions do not replace context-appropriate output escaping. They close a narrow class of attacks that escaping alone cannot address.

References