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

NIP-XX

Open Challenge

draft optional

This NIP defines a Nostr event kind for publicly entering a matchmaking pool for a turn-based, two-player abstract strategy board game of the chess family. The Open Challenge names no specific opponent; it authorizes a designated matchmaker to pair the signer with another player who has published a compatible Open Challenge.

Abstract

Defines kind: 3418, a regular event by which a player enters a matchmaking pool. Unlike the Direct Challenge (kind 3420), which names a specific opponent, the Open Challenge is undirected: it carries the proposed session terms and authorizes a specific matchmaker (and, optionally, a timestamper), but leaves the opponent unspecified. A designated matchmaker pairs two compatible Open Challenges by publishing a Pairing (kind 3419), which declares the session.

Because the opponent is unknown at publication time, an Open Challenge expresses its per-player preferences (variants) by role (self/opponent) rather than by pubkey. The signer’s consent to the eventual pairing is pre-committed here: a Pairing is binding only if it respects the terms that every referenced Open Challenge declared (see kind 3419 §Consent constraints).

Motivation

Initiating a session via Direct Challenge (kind 3420) requires the challenger to know who they want to play. Matchmaking serves the complementary need: “pair me with any available player who matches my terms.” A player enters a pool by publishing a signed, standing authorization — the Open Challenge — and a designated matchmaker pairs two compatible authorizations.

The pool is symmetric: both players publish an Open Challenge, and the matchmaker pairs them. Neither is “the inviter.” The signer pre-authorizes the third parties (matchmaker, and optionally a timestamper) and the session terms, so the matchmaker can consummate the pairing without a separate acceptance signature from either player — while remaining structurally unable to bind a player to terms they did not authorize. This bounds the matchmaker’s power: it chooses who to pair, when, and which concrete values to select within each Open Challenge’s authorization, but it cannot impose an unauthorized timestamper, opponent, variant, time control, or rule system. See kind 3419 §Consent constraints and §Security considerations below.

This NIP carries only the structural and lifecycle aspects of pool entry. Game-specific semantics — what a variant identifier means, how time accounting elapses — are determined by the rule-system document the challenge names (the rules term); the suite designates no arbiter, every rule question being a function of the session’s public events and of that document (kind 3420 §Motivation).

Role-based ownership

In a directed challenge, both participants’ pubkeys are known (the challenger signs; the opponent is named by a p tag), so per-player terms are keyed by pubkey. In an Open Challenge the opponent is anonymous, so per-player preferences are keyed by role instead: self (the signer) and opponent (the sought player). This is the undirected analogue of the implicit-owner seat tag used in the founding events. Ownership becomes pubkey-based again from the Pairing (kind 3419) onward, once the opponent is known.

Specification

Event kind

kind: 3418

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

Authorized third parties

This NIP requires one designated third party — a matchmaker, carried by a dedicated p tag marker, a specific pubkey — plus exactly one timing designation selecting the session’s timing mode per the Canonical Timing NIP §Timing modes and mode selection:

A conforming Open Challenge carries exactly one of the two designations — a timestamper (attested) or one or more timing relays (self-timed) — never both, never neither.

A Pairing is authoritative only if its signer is the matchmaker designated here and its timing designation matches the one made here — the same timestamper in attested mode, the same timing_relay set in self-timed mode — and the same holds for the other Open Challenge it pairs: two Open Challenges are pairable only if their timing designations are identical (kind 3419 §Consent constraints). The designated pubkeys MAY coincide (one operator cumulating roles); combined-role configurations concentrate trust and weaken structural defenses (§Security considerations).

The Open Challenge itself (kind 3418) is never timed: like the Direct Challenge (kind 3420), it is a founding event that designates the session’s timing parties (timestamper, or timing relays). The Pairing (kind 3419) is the timed event of this founding path.

Tags

Authority tags

Tag Cardinality Value Description
p one or two pubkey, optional relay hint, role marker exactly one matchmaker; 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 relay hint (third element) is OPTIONAL but SHOULD be present; when omitted, an empty string MAY be used. The p tags serve a dual purpose: semantic (the authorized third parties) and structural (the matchmaker discovers Open Challenges that authorize it via a standard #p filter).

Constraints on the pubkeys:

The signer (the player entering the pool) is identified by the event’s pubkey, not by a p tag, consistent with the suite convention that participant identity is determined by signature.

Match-terms tags

Tag Cardinality Value Description
game exactly one game identifier the game context for the sought session
variant zero to two role-selector + variant identifier per-role variant preference
time_control one or more duration + optional increment + optional plies the session’s time-control configuration
filter zero or one mode + optional max_delta eligibility filter for the sought opponent
rules exactly one digest + optional retrieval hint the rule-system document the sought session is played under

The game identifier MUST match ^[a-z][a-z0-9]{0,31}$, structured ["game", "<game_id>"].

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

At most one variant tag per role (so at most one self and at most one opponent). The four combinations express the full preference space:

self opponent Meaning
absent absent any variant for either player — the matchmaker chooses both
present absent “I play <self>; the opponent’s variant is the matchmaker’s choice”
absent present “I play whatever; I want to face an <opponent> player”
present present both variants fixed (same-variant if equal, multi-variant if different)

An unspecified role delegates that variant choice to the matchmaker, bounded only by the variants the rule-system document named by rules defines (a set this NIP does not know — see §Validation responsibility). A specified role binds the corresponding variant in any conforming Pairing (kind 3419 §Consent constraints).

The time_control tag follows the format and multi-period semantics of the Direct Challenge (kind 3420 §Match-terms tags): ["time_control", "<duration>", "<increment>", "<plies>"], with trailing optional fields omittable and multiple tags expressing sequential periods of one time-control configuration. This NIP carries a single time-control configuration; a player who wishes to enter pools at several different time controls publishes several Open Challenges. Two Open Challenges are pairable only if their time-control configurations are identical (kind 3419 §Consent constraints).

The rules tag follows the format and semantics of the Direct Challenge (kind 3420 §Match-terms tags): ["rules", "<digest>", "<hint>"], the SHA-256 digest of the rule-system document the signer commits to, with an optional retrieval hint. It is a matching term: the matchmaker MAY pair two Open Challenges only if their rules digests are identical (kind 3419 §Consent constraints) — two players who did not commit to the same rules are not seeking the same game, whatever their game and variant say.

The optional filter tag declares which opponents the signer will accept, reusing the mode vocabulary of the Challenge Policy (kind 30420):

Pinning the rating source makes the rating filter’s satisfaction objective and retroactively verifiable: rating attestations are regular — persistent, retrievable — events, so every verifier reaches the same answer from public data, with no verifier-chosen rating system involved. (The following mode remains verifiable only by consumers who can observe the contact list at the relevant time, the contact list being a replaceable event; see kind 3419 §Consent constraints.)

A conforming Pairing requires each player to satisfy the other’s filter (kind 3419 §Consent constraints). The nobody mode of kind 30420 has no meaning here and MUST NOT be used; a player who wishes to receive no pairings simply publishes no Open Challenge.

No seat tag is carried. Seat assignment for a matchmade session is drawn by the matchmaker in the Pairing (kind 3419 §Match-terms tags) and mirrored by the Game Session (kind 3422), giving every pool pairing a fair, matchmaker-drawn seat. See kind 3419 §From Pairing to Game Session.

Lifecycle tags

Tag Cardinality Value Description
accept_until exactly one unix timestamp (seconds) the latest moment at which a Pairing referencing this Open Challenge is valid
nonce exactly one nonce + difficulty NIP-13 proof of work

The accept_until value MUST be a Unix timestamp strictly greater than the event’s created_at. A Pairing whose canonical timing is greater than this value MUST be treated as invalid by verifiers (see kind 3419 §Consent constraints and Canonical Timing). accept_until therefore doubles as the pool-entry lifetime: a client keeping a player in the pool re-publishes fresh Open Challenges as earlier ones approach expiry. A NIP-40 expiration tag MUST NOT be carried: NIP-40 authorizes relays to delete the event, and a consumed Open Challenge must remain retrievable indefinitely — Pairing verification references it (kind 3419 §Consent constraints), so garbage-collecting an expired-but-consumed entry would amputate the founding evidence of a live session. The deadline every consumer compares is accept_until against canonical timing (Canonical Timing).

The nonce tag follows NIP-13.

Content

The content field MUST be the empty string (""). An Open Challenge carries no narrative payload: it is addressed to no specific person and its semantics are fully expressed by its tags. Forcing the empty string removes any ambiguous side channel, consistent with the Pairing (kind 3419) and Challenge Policy (kind 30420).

Semantic constraints

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

  1. The event contains exactly one matchmaker-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. Each designated pubkey is distinct from the event’s pubkey (the signer).
  2. Exactly one game tag is present, with a valid game identifier matching ^[a-z][a-z0-9]{0,31}$.
  3. Zero, one, or two variant tags are present. Each tag’s second element is self or opponent; at most one tag per role; each tag’s third element is a valid variant identifier matching ^[a-z][a-z0-9]{0,31}$.
  4. At least one time_control tag is present, each with a valid format (per kind 3420 §Match-terms tags).
  5. Zero or one filter tag is present. When present, its mode is one of everyone, following, rating. The everyone and following modes carry no extra value. The rating mode carries exactly four extra values: a max_delta matching ^[1-9][0-9]{0,3}$ and representing a value from 1 to 1000 inclusive, a rating-authority pubkey (64 lowercase hex characters), a rating-attestation kind (3426 or 3427), and a pool scope (pergame or pervariant).
  6. Exactly one accept_until tag is present, with a value strictly greater than created_at.
  7. 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).
  8. The content field is the empty string ("").
  9. Exactly one rules tag is present, whose second element matches ^[0-9a-f]{64}$ (kind 3420 §Match-terms tags).

Constraints 1–9 are all checkable from the event alone.

Validation responsibility

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

Whether a declared variant (for either role) is one the rule-system document actually defines for the game is NOT validated by this NIP — the protocol does not know any game’s variant vocabulary (rule-system agnosticism). A player’s client MUST NOT enter a pool under a rule-system document it cannot retrieve and execute (kind 3420 §Validation responsibility): on this path there is no later acceptance step at which to decline, the Pairing binding both players to found the session.

Separately, a deployment MAY gate the eligibility to set 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 — Same-variant pool entry

Alice enters the sanki ōgi pool at 5+3, against anyone, designating one platform operator as matchmaker, and one timing relay (self-timed mode, the default):

{
 "kind": 3418,
 "pubkey": "<alice_pubkey>",
 "created_at": 1700000000,
 "tags": [
 ["p", "<operator_pubkey>", "wss://relay.example.com", "matchmaker"],
 ["timing_relay", "wss://relay.example.com"],
 ["game", "sanki"],
 ["rules", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com"],
 ["variant", "self", "ogi"],
 ["variant", "opponent", "ogi"],
 ["time_control", "300", "3"],
 ["accept_until", "1700000120"],
 ["nonce", "62473", "16"]
 ],
 "content": "",
 "id": "...",
 "sig": "..."
}

Both self and opponent are fixed to ogi: Alice will only be paired into an ōgi-vs-ōgi session. The short accept_until is a 2-minute pool lifetime; Alice’s client re-publishes while she waits.

Example 2 — “I play ōgi against anyone, rating-restricted”

Bob plays ōgi but does not care what his opponent plays, and only wants opponents within 200 rating points as rated by his pinned rating authority (Elo attestations, kind 3426):

{
 "kind": 3418,
 "pubkey": "<bob_pubkey>",
 "created_at": 1700000000,
 "tags": [
 ["p", "<matchmaker_pubkey>", "wss://relay.example.com", "matchmaker"],
 ["timing_relay", "wss://relay.example.com"],
 ["game", "sanki"],
 ["rules", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com"],
 ["variant", "self", "ogi"],
 ["time_control", "300", "3"],
 ["filter", "rating", "200", "<rating_authority_pubkey>", "3426", "pergame"],
 ["accept_until", "1700000120"],
 ["nonce", "11337", "16"]
 ],
 "content": "",
 "id": "...",
 "sig": "..."
}

The opponent variant is unspecified, so the matchmaker may pair Bob with a player of any variant. The rating filter binds: Bob’s opponent must be within 200 points per the pinned authority’s kind-3426 attestations, and Bob must likewise satisfy the opponent’s filter. The pool the ratings are compared in is the scope the filter declares (pergame here), so the filter binds across any variant combination — Bob may be paired cross-variant and still rating-filtered. Under a pervariant declaration, the filter would instead be satisfiable only by a same-variant pairing (a multi-variant session has no shared (game, variant) pool to compare in).

Example 3 — Fully open (“random variant”)

Carol will play anything against anyone at 3+2:

{
 "kind": 3418,
 "pubkey": "<carol_pubkey>",
 "created_at": 1700000000,
 "tags": [
 ["p", "<matchmaker_pubkey>", "wss://relay.example.com", "matchmaker"],
 ["timing_relay", "wss://relay.example.com"],
 ["game", "sanki"],
 ["rules", "3f6d1a0c9e4b2a7d5c8e1f0a9b3c7d2e4f6a8b0c1d3e5f7a9b2c4d6e8f0a1b3c", "https://blobs.example.com"],
 ["time_control", "180", "2"],
 ["accept_until", "1700000120"],
 ["nonce", "84217", "16"]
 ],
 "content": "",
 "id": "...",
 "sig": "..."
}

No variant tag: the matchmaker chooses both players’ variants (within what the opponent’s Open Challenge allows and what the rule-system document defines).

Client guidelines

Clients implementing kind: 3418 SHOULD:

  1. Let the user enter a pool by publishing an Open Challenge for the chosen (game, variant preferences, time control), with a short accept_until, and re-publish (heartbeat) while the user waits.
  2. Stop re-publishing — and rely on accept_until expiry, optionally a NIP-09 deletion request — when the user leaves the pool. There is no dedicated withdrawal event; an unmatched Open Challenge simply expires.
  3. Surface the designated matchmaker and (when designated) timestamper, and the rule system, to the user; the user SHOULD assess whether they trust these parties before entering the pool (§Security considerations).
  4. When a Pairing (kind 3419) referencing the user’s Open Challenge becomes canonical (per Canonical Timing §Meta-resolution), found the session by publishing the Game Session (kind 3422) unless a canonical one is already observed, and then follow it (kind 3419 §From Pairing to Game Session).
  5. Respect the user’s NIP-51 mute list: do not surface or auto-claim a pairing whose counterparty is muted (matchmakers SHOULD likewise honor this where known).

Matchmaker implementations SHOULD:

  1. Subscribe to kind 3418 with a #p filter on the matchmaker’s pubkey to build the pool of Open Challenges that authorize it.
  2. Pair only Open Challenges that are mutually compatible (same game, identical time_control, satisfiable variant roles, mutually satisfied filters, neither expired) and publish a conforming Pairing (kind 3419).
  3. Avoid pairing the same Open Challenge into more than one session; if a race nonetheless occurs, the greedy rule of kind 3419 §Race resolution — anchored on Canonical Timing — deterministically resolves it.

Security considerations

References