- Sashité for Developers
- Nostr
- NIPs
- 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:
- Matchmaker (marker
matchmaker): the party authorized to pair this Open Challenge with another by publishing a Pairing (kind3419). REQUIRED. - Timestamper (marker
timestamper): the party who provides signed receipt timing by publishing Event Timestamp Attestations (kind3410). Present ⇒ the session is in attested mode. - Timing relay(s) (
timing_relaytags): the relay(s) whose acceptance establishes canonical timing in self-timed mode (the default), each conforming to the Self-Timed Timing Relay profile. Present ⇒ self-timed mode.
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 |
["p", "<matchmaker_pubkey>", "<relay_hint>", "matchmaker"]— REQUIRED.["p", "<timestamper_pubkey>", "<relay_hint>", "timestamper"]— present puts the session in attested mode.["timing_relay", "<wss://relay.example.com>"]— one or more present puts the session in self-timed mode (the default); each designated relay must individually deserve the parties’ trust (Canonical Timing §Security).
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:
- Each of the
matchmakerand (when present)timestamperpubkeys MUST be distinct from the event’spubkey(the signer entering the pool). - The two pubkeys MAY coincide with one another.
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>"]:
- The second element is a role selector, either
self(the signer’s own variant) oropponent(the sought opponent’s variant). - The third element is a variant identifier matching
^[a-z][a-z0-9]{0,31}$.
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):
["filter", "everyone"]— any opponent (the default when the tag is absent).["filter", "following"]— only opponents the signer follows, per the signer’s NIP-02 contact list (kind3). The list consulted is the signer’s most recent contact list whosecreated_atis less than or equal to the Pairing’s canonical timing — the suite’s uniform at-or-before anchoring for consulting a replaceable list (an application applies the same rule to, e.g., a mute list).["filter", "rating", "<max_delta>", "<rating_authority_pubkey>", "<rating_kind>", "<pool_scope>"]— only opponents whose rating is withinmax_deltaof the signer’s.max_deltamatches^[1-9][0-9]{0,3}$and represents a value from 1 to 1000 inclusive (the pattern alone admits up to 9999; the shared bound is fixed here and reused by kind30420§Rating mode). The rating source is pinned by the signer: the fourth element is the pubkey of the rating authority whose attestations are authoritative for this filter, and the fifth is the attestation kind consumed —3426(Elo Rating Attestation) or3427(Glicko-2 Rating Attestation). The sixth element is the pool scope the ratings are compared in, declared by the signer:pergame(all the authority’s attestations carrying the session’sgame, regardless ofvarianttags) orpervariant(only attestations whosegameand singlevarianttag match the player’s resolved variant in the Pairing). A player’s rating, for this filter, is the display-scale rating carried by the most recent conforming attestation signed by that authority for that player in the declared scope, withcreated_atless than or equal to the Pairing’s canonical timing (“most recent” per the kind’s §Client guidelines: largestcreated_at, smallest event id as tiebreaker). A player with no such attestation is unrated for this filter, and the filter is NOT satisfied (fail-closed, consistent with kind30420§Rating mode and external rating data). A signer SHOULD declare the scope their pinned authority actually computes (documented by that authority); a mismatched declaration misconstrains only the signer’s own pairings — the filter’s evaluation stays objective either way.
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:
- The event contains exactly one
matchmaker-markedptag and exactly one timing designation: either exactly onetimestamper-markedptag and notiming_relaytag (attested mode), or notimestamperand one or moretiming_relaytags each carrying awss://URL (self-timed mode, the default) — per the Canonical Timing NIP §Timing modes and mode selection. Each designated pubkey is distinct from the event’spubkey(the signer). - Exactly one
gametag is present, with a valid game identifier matching^[a-z][a-z0-9]{0,31}$. - Zero, one, or two
varianttags are present. Each tag’s second element isselforopponent; at most one tag per role; each tag’s third element is a valid variant identifier matching^[a-z][a-z0-9]{0,31}$. - At least one
time_controltag is present, each with a valid format (per kind3420§Match-terms tags). - Zero or one
filtertag is present. When present, its mode is one ofeveryone,following,rating. Theeveryoneandfollowingmodes carry no extra value. Theratingmode carries exactly four extra values: amax_deltamatching^[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 (3426or3427), and a pool scope (pergameorpervariant). - Exactly one
accept_untiltag is present, with a value strictly greater thancreated_at. - Exactly one
noncetag 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). - The
contentfield is the empty string (""). - Exactly one
rulestag is present, whose second element matches^[0-9a-f]{64}$(kind3420§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:
- 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. - Stop re-publishing — and rely on
accept_untilexpiry, optionally a NIP-09 deletion request — when the user leaves the pool. There is no dedicated withdrawal event; an unmatched Open Challenge simply expires. - 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).
- 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 (kind3422) unless a canonical one is already observed, and then follow it (kind3419§From Pairing to Game Session). - 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:
- Subscribe to kind
3418with a#pfilter on the matchmaker’s pubkey to build the pool of Open Challenges that authorize it. - Pair only Open Challenges that are mutually compatible (same
game, identicaltime_control, satisfiable variant roles, mutually satisfied filters, neither expired) and publish a conforming Pairing (kind3419). - 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
- Signatures: Open Challenge events are signed by the player entering the pool. Consumers MUST verify signatures per NIP-01.
- Proof of work: the
noncetag (NIP-13) limits pool spam. Relays SHOULD enforce a minimum difficulty. - Trust model: designating a matchmaker (and optionally a timestamper) implies trust that they will pair fairly, draw the seats fairly, time honestly, and stay responsive; nothing on the rules axis is trusted, verdicts being recomputable under
rules. The matchmaker’s power is bounded by the consent constraints of kind3419: it cannot bind terms the signer did not authorize. The fuller analysis — the matchmaker’s bounded discretion, its liveness latitude (whether and when to pair), and the concentration risk when designated roles coincide — belongs to the consuming application’s documentation; this suite’s application layer maintains one. - Mute lists: NIP-51 mute lists complement the
filtertag; thefilterconstrains by population (following / rating), the mute list excludes specific pubkeys. - Privacy: an Open Challenge publicly reveals the signer’s pubkey, the pool they are seeking, and their activity timing — irreducible for a public pool. A client SHOULD make the disclosure explicit before the first publication; the full enumeration of direct and inferable disclosures belongs to the consuming application’s documentation.
- Founding scope: this NIP defines pool entry only. The pairing, founding, ply exchange, and conclusion are defined by other NIPs in the suite (Pairing, Game Session, Ply, Conclusion).
References
- NIP-01 — Basic protocol flow description
- NIP-02 — Follow List (the
followingfilter mode) - NIP-13 — Proof of Work
- NIP-51 — Lists
- Kind
3419— Pairing (the matchmaker event that pairs two Open Challenges) - Kind
3426— Elo Rating Attestation / Kind3427— Glicko-2 Rating Attestation — the pinnable rating sources for theratingfilter mode - Kind
3420— Direct Challenge (the directed founding alternative) - Kind
3422— Game Session (founds the session on the Pairing; signed by either player) - Event Timestamp Attestation — primitive for the Pairing’s authoritative timing (attested mode)
- Self-Timed Timing Relay — the relay profile behind the
timing_relaydesignation - Canonical Timing — timing modes, mode selection, canonical timing, meta-resolution
