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 arbiter (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, arbiter, 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 arbiter, timestamper, opponent, variant, or time control. 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 designated arbiter’s rule system.

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 two designated third parties — a matchmaker and an arbiter, each carried by a dedicated p tag marker, each 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, its arbiter is the arbiter 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 two or three pubkey, optional relay hint, role marker exactly one matchmaker; exactly one arbiter; 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

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 arbiter’s rule system supports (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 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 performed by the arbiter when it publishes the Game Session (kind 3422), giving every pool pairing a fair, arbiter-assigned 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 Adjudication Request (kind 3424) 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, exactly one arbiter-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 ("").

Constraints 1–8 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 designated arbiter actually supports for the game is NOT validated by this NIP — the protocol does not know any game’s variant vocabulary (rule-system agnosticism). A pairing whose terms the arbiter cannot honor fails at the ratification step (the arbiter declines to publish a Game Session); see kind 3422 §Handling a silent arbiter. Clients SHOULD pre-validate terms against the arbiter’s published capabilities when known.

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 both matchmaker and arbiter, 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"],
 ["p", "<operator_pubkey>", "wss://relay.example.com", "arbiter"],
 ["timing_relay", "wss://relay.example.com"],
 ["game", "sanki"],
 ["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"],
 ["p", "<arbiter_pubkey>", "wss://relay.example.com", "arbiter"],
 ["timing_relay", "wss://relay.example.com"],
 ["game", "sanki"],
 ["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"],
 ["p", "<arbiter_pubkey>", "wss://relay.example.com", "arbiter"],
 ["timing_relay", "wss://relay.example.com"],
 ["game", "sanki"],
 ["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 arbiter supports).

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, arbiter, and (when designated) timestamper 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), transition the UI to “matched; awaiting arbiter” and then follow the Game Session (kind 3422).
  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