Sashité for Developers
  1. Sashité for Developers
  2. Nostr
  3. NIPs
  4. Game Session

NIP-XX

Game Session

draft optional

Abstract

Defines kind: 3422, a regular event published, once the session’s founding is complete, by the session’s designated arbiter. The Game Session carries the session’s two players, their seat assignments, their per-player variants, the game family, and the initial state of play — including the initial board position for variants that require it (e.g., chess960, shogi960) — and constitutes an explicit ratification of the session parameters by its signer. The founding follows one of three paths that converge here — a one-to-one Direct Challenge / Accepted Challenge (kinds 3420/3421), a matchmaking Open Challenge / Pairing (kinds 3418/3419), or a Rematch Offer (kind 3430, a mutually addressed pair from a concluded session’s two players — §Rematch founding) — each yielding exactly two players, seated first and second (the protocol’s seat vocabulary, kind 3420 §Match-terms tags); the variant vocabulary is the rule system’s. No Ply (kind 3423) is valid until a conforming Game Session is published.

Motivation

The lifecycle of a session has a transition between “parameters declared” (after the founding) and “playable” (when the players can submit Plies). Two concerns motivate making this transition explicit, signed by the arbiter:

  1. Initial position establishment. Some variants (chess960, shogi960, and other randomized-setup variants) require an initial position that is unknown until the session begins. The randomization must be authoritative: neither player can choose a favorable starting position. The arbiter — already trusted to rule on the session outcome — is the natural signer of this initial position.

  2. Explicit arbiter ratification. Before a session begins, the arbiter should confirm they accept the session parameters (the game, the chosen variants, the seat assignments, the time control, the players). Without an explicit ratification step, a session might start before the arbiter even acknowledges it — leading to plays that the arbiter later cannot or will not adjudicate. The Game Session is the arbiter’s signed commitment to officiating the session. For a matchmaking founding (Pairing, kind 3419), where the seat is deliberately left open, the arbiter additionally assigns each player a seat at this step — typically a fair random draw, the same authoritative-randomization act as choosing a chess960 position (see §Seats).

By unifying these two concerns into one event, the protocol provides a clear and uniform “the arbiter is ready” milestone for every session, applicable both to deterministic-setup variants (where the content is empty) and randomized-setup variants (where the content carries the initial position).

Time accounting implication

The Game Session defines the session-start moment (t₀) used for time-control accounting: its optional start_at tag when present, otherwise its canonical timing (§Canonical session start). Before t₀, no Ply is valid; at t₀, the clock for the player expected to move first starts running. The detailed t₀ semantics — including the start_at override and the role of canonical timing — are specified in §Canonical session start below.

Specification

Event kind

kind: 3422

Game Session events are regular events per NIP-01: immutable once signed and broadcast.

Signing party

The event MUST be signed by the pubkey designated by its founding context. In this suite, for a directed session that is the arbiter-marked pubkey of the Accepted Challenge (kind 3421), for a matchmade session the arbiter-marked pubkey of the Pairing (kind 3419), and for a rematch session the arbiter-marked pubkey shared by the two Rematch Offers (kind 3430) — necessarily the arbiter that ruled the concluded session (§Rematch founding). The arbiter founds the Game Session on all three paths. Any other signer’s event referencing the same founding with kind 3422 is invalid and MUST be ignored by verifiers.

Tags

Founding reference

Tag Cardinality Value Description
e one or two (path-dependent) event id, optional relay hint, marker reference(s) to the session’s founding event(s)

The Game Session MUST reference, via e tag(s), the founding event(s) that established the session and designated its arbiter. The marker (fourth element) names the founding event type and is defined by the consuming application. In this suite, the founding is one of three alternatives — a single founding event on the directed and matchmade paths, or a mutually addressed pair on the rematch path (never mixed):

Each founding event designates the arbiter (via an arbiter-marked p tag), carries the session’s timing designation (a timestamper-marked p tag in attested mode, or timing_relay tags in self-timed mode — Canonical Timing §Timing modes and mode selection), and establishes the two players — on the rematch path the pair of Offers does so jointly (§Rematch founding) — so the Game Session’s signing-party and player rules apply uniformly regardless of path; seat and variant sourcing differ by path (see §Seats, §Variants, and §Rematch founding). The relay hint (third element) is OPTIONAL but SHOULD be present.

Timing designation (mirrored)

Tag Cardinality Value Description
p timestamper zero or one pubkey, optional relay hint, marker timestamper in attested mode, the designated timestamper, mirrored from the founding
timing_relay zero or more relay URL (wss://…) in self-timed mode, the designated timing relays, mirrored from the founding verbatim

The Game Session repeats the founding’s timing designation — exactly one of the two forms — so that it is self-contained for verifiers: a consumer holding only the Game Session knows the session’s timing mode and which parties (timestamper, or relays) establish canonical timing for every event of the session, without first resolving the founding chain.

Game

Tag Cardinality Value Description
game exactly one game-family identifier the game family of the session

The Game Session MUST carry exactly one game tag identifying the game family. The identifier MUST match the regular expression ^[a-z][a-z0-9]{0,31}$. The session’s game is the arbiter’s ratified statement of the family and MUST equal the game declared in the referenced founding event(s).

Example values: chess, shogi, xiangqi, go, or — in this suite — sanki (a multi-game 8×8 platform).

Players

Tag Cardinality Value Description
p exactly two pubkey, optional relay hint, marker player a session player

The Game Session carries one p tag per player, each with the marker player as its fourth element:

The set of player pubkeys MUST be consistent with the players established by the referenced founding event(s). The p tags serve a dual purpose: semantic (identifying the players) AND structural (notification routing via standard Nostr #p filters, so each player receives the Game Session without chained subscription logic).

All three founding paths — directed (kinds 3420/3421), matchmade (kinds 3418/3419), rematch (kind 3430) — establish exactly two players, so a Game Session carries exactly two p tags.

Seats

Tag Cardinality Value Description
seat exactly one per player pubkey, seat-name the player’s seat assignment

The Game Session MUST carry exactly one seat tag per player. The second element is the player’s pubkey; the third is the seat-name, one of exactly two protocol-defined values: first (moves first) or second (moves second). The two players MUST hold the two distinct values. Seat-names are play-order indices, not game vocabulary; the traditional labels (white/black, sente/gote, red/black) are presentation, mapped by the rule system (kind 3420 §Match-terms tags).

The source of the seat assignment depends on the founding path:

Variants

Tag Cardinality Value Description
variant exactly two (one per player) pubkey, variant-name the player’s variant within the game family

The Game Session MUST carry exactly one variant tag per player. Each variant tag is structured ["variant", "<player_pubkey>", "<variant_name>"]. The second element is a player pubkey; the third is a variant identifier matching ^[a-z][a-z0-9]{0,31}$.

Each player’s variant MUST equal that player’s variant declared in the referenced founding event(s). On the rematch path the founding Offers carry no variant (they inherit — kind 3430 §Inherited terms); each player’s variant MUST instead equal that player’s variant in the concluded session the Offers reference (§Rematch founding).

The requiredness is unconditional — a deliberately structural rule, so that conformance is countable without knowing anything about the game: a game family with a single variant simply repeats its one style’s identifier (e.g., ["variant", "<pubkey>", "chess"] in a plain chess session). The identifiers’ meaning stays opaque to the protocol; the arbiter’s rule system interprets them (for sanki, each player’s variant — chess, ogi, xiongqi — determines their setup and movement rules on the shared board).

Scheduled start (optional)

Tag Cardinality Value Description
start_at zero or one unix seconds the scheduled session-start instant (t₀); overrides canonical timing

A Game Session MAY carry a start_at tag — a single unix-seconds instant declaring when the session’s clock starts:

When present, start_at MUST be the event’s created_at (a session cannot schedule a start before it exists — locally verifiable), and it becomes the session’s t₀: no Ply is valid before it, and the game cannot begin before that instant. When absent, t₀ is the session’s canonical timing (§Canonical session start) — so start_at is fully backward-compatible and optional on every founding path (directed, matchmade, rematch).

Because t₀ is shared by both players, a start_at shifts both clocks identically and cannot create an asymmetric advantage. A session on any founding path MAY use it to schedule a start ahead of publication.

Rematch founding

This section applies only to a Game Session whose founding references are marked rematch_offer (kind 3430). A rematch Game Session realizes a new game between the same two players as a concluded session, with the seats swapped and every other term reused. Unlike the directed and matchmade paths — a single founding event — the rematch path has two founding references: the mutually addressed pair of Rematch Offers (kind 3430) by which the two players each consented (kind 3430 §Rematch founding). The arbiter that ruled the concluded session is both the binder and the founder — it already holds the terms, so nothing is redeclared. That the concluded session is in fact concluded is not taken on trust either: each Offer carries a concluded_by reference to an Adjudication (kind 3425) of it, so a verifier establishes the precondition from the events rather than from the arbiter’s word (kind 3430 §Tags).

For this path the semantic constraints below apply with these substitutions: constraint 1’s founding reference is the pair of Rematch Offers (kind 3430, marked rematch_offer) rather than a single event; constraint 2’s signer is those Offers’ shared arbiter; constraint 3’s game, constraint 5’s seats (here swapped), and constraint 6’s per-player variant all resolve against the concluded session the Offers reference; and constraint 9 (the rematch deadline) applies. The canonical rematch for a concluded session is the first valid arbiter-signed Game Session for the slot (rematch, concluded session) (§Idempotence and race resolution; Canonical Timing §Meta-resolution).

Content

The content field MUST be a non-empty string representing the initial position of the session. The encoding format is application-specific and determined by the consuming application based on the session’s game and variant tags. Common encoding formats include:

The protocol does NOT prescribe any particular encoding, does NOT validate the content’s semantic well-formedness, and does NOT interpret it. Recognition of the encoding (from the declared game and variant), parsing, and validation against the variant’s rule system are the responsibility of the consuming application.

Requiring an always-present initial position encoding — rather than allowing an implicit “default position” for deterministic variants — eliminates ambiguity. Every Game Session is self-describing: a consumer can reconstruct the session’s starting state from the event alone, given the declared encoding format, without depending on hardcoded conventions about what “the standard position of variant X” means. This also generalizes uniformly to variants whose starting position is randomized (chess960), custom (handicap setups), or game-specific (e.g., the reference sanki rule system, where the multi-style nature of the game makes a “default” hard to define).

The content field MUST consist of valid UTF-8, contain no C0/C1 control characters (except those explicitly permitted by the declared encoding), and contain no Unicode bidirectional override characters. The total length of the content field MUST NOT exceed 4096 bytes.

Semantic constraints

A conforming Game Session event MUST satisfy all of the following:

  1. The event’s founding reference is conforming: exactly one e tag on the directed and matchmade paths, or exactly two rematch_offer-marked e tags on the rematch path, each referencing a retrievable, signature-valid, conforming founding event (per that kind’s own constraints — §Semantic constraints for an Accepted Challenge or a Rematch Offer, §Consent constraints for a Pairing). (In this suite: the Accepted Challenge, kind 3421, marked accepted_challenge, for a directed session; the canonical Pairing, kind 3419, marked pairing, for a matchmade session; or a matched pair of Rematch Offers, kind 3430, marked rematch_offer, for a rematch session — §Rematch founding.)
  2. The event’s pubkey (the signer) MUST match the arbiter designated by that founding event. (In this suite: the arbiter-marked pubkey of the Accepted Challenge, of the Pairing, or shared by the pair of Rematch Offers.)
  3. The event contains exactly one game tag, with a valid identifier per §Game, whose value MUST equal the game declared in the referenced founding event(s).
  4. The event contains exactly two p tags carrying the marker player, with distinct pubkeys, matching the two players established by the founding event(s).
  5. The event contains exactly one seat tag per player. Each seat tag’s second element is a player pubkey (per constraint 4); the seat-names are first and second, one each (§Seats). For a directed founding (Accepted Challenge, kind 3421), the seat assignment MUST be consistent with the seat tag carried by the founding pair — by the Direct Challenge or, when the Direct Challenge omits it, by the Accepted Challenge (per kind 3421 §Semantic constraints): the declaring player holds the declared value, the other player the other value. For a matchmaking founding (Pairing, kind 3419), which carries no seat, the arbiter assigns the two values (see §Seats); no founding seat constrains the assignment. For a rematch founding (Rematch Offers, kind 3430), the seats MUST be swapped relative to the concluded session the Offers reference (§Rematch founding).
  6. The event contains exactly two variant tags, one per player pubkey (per constraint 4). Each variant’s third element MUST equal that player’s variant declared in the referenced founding event(s) — for a rematch, in the concluded session the Offers reference, the Offers themselves carrying none (§Rematch founding).
  7. The content field is a non-empty string per §Content, with length at most 4096 bytes.
  8. If a start_at tag is present, it is a single unix-seconds value the event’s created_at; t₀ is then the later of it and the canonical timing (§Canonical session start).
  9. The event’s timing designation mirrors the founding’s verbatim: the same timestamper-marked p tag (attested mode) or the same timing_relay set (self-timed mode), and exactly one of the two forms. Rematch path only: the event’s canonical timing is at or before the earlier of the two referenced Offers’ accept_until values; a rematch Game Session whose canonical timing exceeds it is non-conforming (kind 3430 §Lifecycle tags).

Constraints 1–4, 6, and 9 require cross-event validation against the referenced founding event (directed: the Accepted Challenge, and possibly the Direct Challenge, depending on which carries the relevant tags, per kind 3421 §Semantic constraints; matchmade: the Pairing, kind 3419, and the two Open Challenges it references; rematch: the pair of Rematch Offers, kind 3430, and — for the game, variants and swapped seats — the concluded Game Session they reference). Constraint 5 is a cross-event check for a directed founding (consistency with the seat carried by the Direct Challenge or the Accepted Challenge) and for a rematch founding (the swap of the concluded session’s seats), and an event-local check for a matchmade founding (the arbiter’s own opposite-seat assignment). Constraint 7 is checkable from the event alone. The Game Session’s created_at is informational and is not subject to any cross-event semantic constraint; ordering is enforced structurally by the cross-event reference chain (the Game Session references the founding event by event ID, which proves it was signed after the founding event existed).

Idempotence and race resolution

A given session has exactly one canonical Game Session. If the arbiter publishes multiple Game Session events for the same session — the same founding reference for a directed (accepted_challenge) or matchmade (pairing) session, or the same (rematch, concluded session) slot for a rematch session (one concluded session founds at most one rematch, however many Rematch Offers its players publish — §Rematch founding) — the canonical event is determined per Canonical Timing §Meta-resolution: the candidate with the smallest canonical timing, with the smallest event ID as tiebreaker.

Non-canonical Game Sessions are ignored by verifiers. The arbiter SHOULD publish only one Game Session per session; publishing multiple is non-conforming behavior and may indicate arbiter misbehavior or implementation bugs. In attested mode, the Game Session’s own created_at is informational and MUST NOT be used for canonicalization; in self-timed mode it IS the canonical timing (made non-backdatable by the designated timing relay).

Validation responsibility

Validation of a Game Session against the semantic constraints is the responsibility of the client (consuming players’ clients, downstream observers, rating verifiers). The arbiter is trusted to publish a conforming event; an event from the arbiter that fails semantic constraints is structurally invalid and MUST be rejected.

A session without a conforming Game Session remains in the “declared but not playable” state. No Ply (kind 3423) can be valid for such a session: Ply validity requires t₀, and t₀ requires both a conforming Game Session and its canonical timing (§Canonical session start; kind 3423 §Time accounting). Plies whose canonical timing precedes t₀ MUST be treated as invalid.

Canonical session start

The session-start timestamp (t₀), used by time-control accounting (see kind 3423 §Time accounting), is:

t₀ = the later of the Game Session’s start_at tag value (when present) and the Game Session’s canonical timing (Canonical Timing §t₀).

With no start_at, t₀ is simply the canonical timing. With a future start_at, the session is scheduled and no clock runs before it. And if canonical timing is established only after start_at has passed, t₀ is that later moment — no player is ever charged for an interval during which no valid Ply existed.

The canonical timing is, always, the timing that orders the session and establishes its existence. In attested mode it is the created_at of the Event Timestamp Attestation (kind 3410) signed by the designated timestamper and referencing this Game Session via an attests-marked e tag (the Session Start Attestation). In self-timed mode it is the Game Session’s own created_at, once its acceptance by a designated timing relay is established. Whichever branch yields t₀, the result is authoritative for all time-control accounting — rule-system-defined; the reference sanki rule system specifies it in its supporting document (Time Accounting — Sanki).

In attested mode, the Game Session’s own created_at is informational and MUST NOT be used as the canonical timing or as input to any timing-dependent decision (a present start_at still defines t₀); in self-timed mode the created_at is the canonical timing — and t₀ when no start_at is present.

The structure of the Session Start Attestation is:

["e", "<game_session_event_id>", "<relay_hint>", "attests"]

Only the attests-marked e tag is required. NIP-26-delegated signatures from the timestamper are NOT accepted as authoritative; see kind 3420 §Security considerations.

Until the Game Session has canonical timing, it is pending (Canonical Timing §The pending state) and the session is in an initialized but untimed state: distinct from declared but not initialized (before the Game Session) and from playable (after t₀ exists and has been reached). No Ply is valid in this window — operationally, a Ply is early or late by comparing its own canonical timing to t₀; no wall-clock observer is involved. In attested mode, the Game Session acquires canonical timing when the designated timestamper attests it — a window bounded only by the timestamper’s responsiveness (§Handling a silent timestamper below). In self-timed mode, canonical timing is established the moment a designated timing relay accepts the Game Session, so the window is only propagation delay — unless no designated relay accepts it (§Handling a silent timing relay below).

No founding event’s created_at defines t₀. On the directed path, the Direct Challenge constrains the lifecycle deadline (accept_until) and the Accepted Challenge contributes to lifecycle ordering via its own canonical timing; on the matchmaking path, the Open Challenge constrains the deadline and the Pairing contributes ordering via its own canonical timing. None of these contributes to time-control accounting, which anchors on the Game Session’s start_at (when present) or else its canonical timing.

Examples

The examples below use placeholder pubkeys, event ids, and timestamps for illustration. They assume reference to a corresponding Accepted Challenge event (kind 3421).

Example 1 — Standard chess starting position (FEN encoding)

The session is game="chess", both players play variant="chess". The arbiter (Carol) signs the Game Session with the standard chess starting position encoded as a FEN string:

{
 "kind": 3422,
 "pubkey": "<carol_pubkey>",
 "created_at": 1700000400,
 "tags": [
  ["e", "<acceptance_event_id>", "wss://relay.example.com", "accepted_challenge"],
  ["timing_relay", "wss://relay.example.com"],
  ["game", "chess"],
  ["p", "<alice_pubkey>", "wss://relay.example.com", "player"],
  ["p", "<bob_pubkey>", "wss://relay.example.com", "player"],
  ["seat", "<alice_pubkey>", "first"],
  ["seat", "<bob_pubkey>", "second"],
  ["variant", "<alice_pubkey>", "chess"],
  ["variant", "<bob_pubkey>", "chess"]
 ],
 "content": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
 "id": "...",
 "sig": "..."
}

The content carries the standard chess starting position in FEN. Even though the position is conventionally well-known, the protocol requires it to be explicit: the Game Session is self-describing. The session becomes playable once this event has canonical timing — its Session Start Attestation in attested mode, or acceptance by a designated timing relay in self-timed mode — establishing t₀ per §Canonical session start; at that point, the player assigned to seat first (Alice in this example) submits the first Ply.

Example 2 — Randomized initial position (chess960)

The session is game="chess", both players play variant="chess960". The arbiter selects starting position 518 (the standard chess position, by coincidence — one of 960 possible positions) and encodes it as a FEN string:

{
 "kind": 3422,
 "pubkey": "<carol_pubkey>",
 "created_at": 1700000400,
 "tags": [
  ["e", "<acceptance_event_id>", "wss://relay.example.com", "accepted_challenge"],
  ["timing_relay", "wss://relay.example.com"],
  ["game", "chess"],
  ["p", "<alice_pubkey>", "wss://relay.example.com", "player"],
  ["p", "<bob_pubkey>", "wss://relay.example.com", "player"],
  ["seat", "<alice_pubkey>", "first"],
  ["seat", "<bob_pubkey>", "second"],
  ["variant", "<alice_pubkey>", "chess960"],
  ["variant", "<bob_pubkey>", "chess960"]
 ],
 "content": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
 "id": "...",
 "sig": "..."
}

The content carries a FEN representation of the chosen initial position. Both players’ clients interpret this string per chess960 conventions to display the starting position.

Example 3 — Sashité Sanki session (FEEN encoding)

The session is game="sanki", Alice plays variant="chess" and Bob plays variant="ogi" (a multi-variant session). The reference arbiter signs the Game Session with the multi-variant starting position encoded as a FEEN string:

{
 "kind": 3422,
 "pubkey": "<arbiter_pubkey>",
 "created_at": 1700000400,
 "tags": [
  ["e", "<acceptance_event_id>", "wss://relay.example.com", "accepted_challenge"],
  ["timing_relay", "wss://relay.example.com"],
  ["game", "sanki"],
  ["p", "<alice_pubkey>", "wss://relay.example.com", "player"],
  ["p", "<bob_pubkey>", "wss://relay.example.com", "player"],
  ["seat", "<alice_pubkey>", "first"],
  ["seat", "<bob_pubkey>", "second"],
  ["variant", "<alice_pubkey>", "chess"],
  ["variant", "<bob_pubkey>", "ogi"]
 ],
 "content": "<FEEN string encoding Alice's chess setup and Bob's ogi setup on a shared 8×8 board; see Position Encoding — Sanki for the concrete encoding>",
 "id": "...",
 "sig": "..."
}

The content carries a FEEN representation of the multi-variant initial position. FEEN’s three-field structure (piece-placement, hands, style-turn) accommodates Sanki’s defining property: two players using different variants on a shared board (the exact profile is the rule system’s supporting document, Position Encoding — Sanki).

Example 4 — Matchmade session (Pairing founding, arbiter-assigned seats)

The session was founded through the matchmaking pool: the arbiter references the canonical Pairing (kind 3419) with the pairing marker instead of an Accepted Challenge, and — because the matchmaking path carries no seat — assigns the seats itself (here, a random draw gave Alice second and Bob first):

{
 "kind": 3422,
 "pubkey": "<arbiter_pubkey>",
 "created_at": 1700000400,
 "tags": [
  ["e", "<pairing_event_id>", "wss://relay.example.com", "pairing"],
  ["timing_relay", "wss://relay.example.com"],
  ["game", "sanki"],
  ["p", "<alice_pubkey>", "wss://relay.example.com", "player"],
  ["p", "<bob_pubkey>", "wss://relay.example.com", "player"],
  ["seat", "<alice_pubkey>", "second"],
  ["seat", "<bob_pubkey>", "first"],
  ["variant", "<alice_pubkey>", "ogi"],
  ["variant", "<bob_pubkey>", "ogi"]
 ],
 "content": "<FEEN string for the ōgi-vs-ōgi starting position>",
 "id": "...",
 "sig": "..."
}

Everything downstream of this event is identical to a directed session. The players’ variants are those resolved by the Pairing; the seats are the arbiter’s draw, authoritative from this event (no founding seat exists to mirror).

Example 5 — Rematch session (Rematch Offer founding, swapped seats)

The two players of a concluded session (Example 3, where Alice held first/chess and Bob held second/ogi) each published a Rematch Offer (kind 3430). The arbiter references both Offers with the rematch_offer marker — the one path with two founding references — and founds the rematch with the seats swapped (Bob now first, Alice now second), reusing the concluded session’s game and per-player variants:

{
 "kind": 3422,
 "pubkey": "<arbiter_pubkey>",
 "created_at": 1700010100,
 "tags": [
  ["e", "<offer_alice_id>", "wss://relay.example.com", "rematch_offer"],
  ["e", "<offer_bob_id>", "wss://relay.example.com", "rematch_offer"],
  ["timing_relay", "wss://relay.example.com"],
  ["game", "sanki"],
  ["p", "<alice_pubkey>", "wss://relay.example.com", "player"],
  ["p", "<bob_pubkey>", "wss://relay.example.com", "player"],
  ["seat", "<bob_pubkey>", "first"],
  ["seat", "<alice_pubkey>", "second"],
  ["variant", "<alice_pubkey>", "chess"],
  ["variant", "<bob_pubkey>", "ogi"]
 ],
 "content": "<FEEN string for the swapped-seat starting position>",
 "id": "...",
 "sig": "..."
}

Everything downstream of this event is identical to a directed session. The game and per-player variants are inherited from the concluded session; the seats are its own, swapped; and the slot is (rematch, <concluded_session_id>), so this rematch is canonical however many Offers the two players published (§Idempotence and race resolution; kind 3430 §Race conditions).

Client guidelines

Clients implementing kind: 3422 SHOULD:

  1. For players’ clients: do NOT allow the user to submit a Ply (kind 3423) until t₀ is established (see §Canonical session start). This requires a conforming Game Session with canonical timing — in attested mode, the Session Start Attestation from the designated timestamper; in self-timed mode, acceptance by a designated timing relay. Display distinct indicators for the two pre-playable states:
    • Awaiting Game Session (after the founding — Accepted Challenge or canonical Pairing — no Game Session yet): “waiting for arbiter to start the session”.
    • Awaiting authoritative timing (Game Session received but not yet canonically timed — in attested mode, no Session Start Attestation yet): “initialized; waiting for authoritative timing”.
  2. For arbiter implementations: after observing a valid founding referencing this arbiter — an Accepted Challenge (kind 3421) for a directed session, a canonical Pairing (kind 3419) for a matchmade session, or a matched pair of Rematch Offers (kind 3430, both referencing a concluded session this arbiter ruled) for a rematch — validate the session parameters (game, variant, time control, etc.). For a matchmaking founding, also assign the two players opposite seats (typically at random); for a rematch founding, swap the concluded session’s seats and reuse its game, variants, and time control (§Rematch founding). If accepted, publish the Game Session within a reasonable time; if the parameters cannot be honored, the arbiter SHOULD NOT publish a Game Session (effectively declining the session by silence).
  3. For all clients: parse the Game Session’s content per the variant’s encoding conventions to display the initial position correctly. Recognition of the encoding is by the declared game and variant; common encodings include FEN, SFEN, and FEEN.
  4. For rating verifiers and observers: a session without a conforming Game Session has no effect on ratings (no Ply is valid, no contributing Adjudication can be produced).

Handling a silent arbiter

If the designated arbiter never publishes a Game Session, the session remains in a “declared but not initialized” state indefinitely. This is a recognized but unrecoverable state at the protocol level: there is no built-in timeout, no fallback arbiter, and no void mechanism. The session is simply dormant — it has no effect on ratings, no Ply can be valid, and no Adjudication will be produced.

Players SHOULD treat the absence of a Game Session within a clock-reasonable interval (typically minutes to hours, depending on the arbiter’s published service level) as a strong signal that the arbiter has declined the session. To formally close the dormant state, the founders’ clients SHOULD display the session as “declined by arbiter” and offer the players the option to start a fresh session (a new Direct Challenge), possibly with a different arbiter. The dormant Acceptance event remains on Nostr indefinitely but carries no protocol-level consequence.

This behavior is consistent with the principle that the arbiter is a designated trusted party (§Security considerations). The principal mitigation against silent arbiters is selection: players SHOULD only accept challenges whose designated arbiter they trust to be operational.

Handling a silent timestamper (attested mode)

This applies in attested mode. After the Game Session has been published, the designated timestamper is expected to publish the Session Start Attestation. If they never do, t₀ is never established and the session remains in the “initialized but untimed” state indefinitely. This is structurally analogous to a silent arbiter at the Game Session step: the session is dormant, no Ply is valid, no Adjudication can produce a contributing verdict for rating purposes (Plies are required for any non-trivial verdict context, and Plies require t₀).

Players SHOULD treat the absence of a Session Start Attestation within a clock-reasonable interval as a strong signal that the timestamper is unavailable. To formally close this dormant state, the founders’ clients SHOULD display the session as “timestamper unavailable” and offer the players the option to start a fresh session (a new Direct Challenge) with a different timestamper. The dormant Game Session remains on Nostr indefinitely but carries no protocol-level consequence.

As with the silent-arbiter case, the principal mitigation is selection: players SHOULD only accept challenges whose designated timestamper they trust to be operational. A timestamper that goes silent after play has begun is a distinct case with no specified recovery — a known limitation of attested mode; a recovery path must be specified before a deployment activates the mode.

Handling a silent timing relay (self-timed mode)

In self-timed mode t₀ is established as soon as one of the designated timing relays accepts the Game Session, so there is normally no separate wait. The analogue of a silent timestamper is a designated set none of whose relays will accept the Game Session — because they are unreachable, or reject the event (for example, a created_at their clocks deem in the past). Until a designated relay accepts it, the Game Session is pending and t₀ is undefined — exactly the initialized but untimed state above. Players SHOULD treat persistent rejection or unavailability as a signal that the designated relays are unusable, and offer to start a fresh session designating working relays. The mitigation is the same as for every designated party: designate relays you trust to be operational and correctly configured (Canonical Timing §Security).

Security considerations

References