Sashité for Developers
  1. Sashité for Developers
  2. Nostr
  3. NIPs
  4. Adjudication

NIP-XX

Adjudication

draft optional

This NIP defines a Nostr event kind for the designated arbiter of a chess-family session to publish a binding ruling — a verdict that terminates the session. The Adjudication is the response to an Adjudication Request and constitutes the cryptographically-final outcome of the session.

Abstract

Defines kind: 3425, a regular event by which the designated arbiter of a chess-family session publishes a binding verdict. The event references the Game Session (kind 3422) and the event that triggered the ruling — an Adjudication Request (kind 3424). It assigns a numeric result to each player via tags and identifies the termination cause via the content field.

An Adjudication terminates the session. No further events can alter the outcome.

Motivation

A session in the chess family is decided by the designated arbiter’s ruling. The Adjudication is the public, signed verdict that:

The verdict is reactive: an Adjudication MUST reference an Adjudication Request (kind 3424) via its triggered_by tag (see §Semantic constraints). The arbiter cannot publish a verdict for a session no player has invoked. Even when the session has clearly concluded by the rule system, the verdict only materializes in response to a player’s invocation. This is a deliberate structural property: the arbiter is a reactive authority that rules on what is invoked, not on what it spontaneously observes. The asymmetry preserves player agency in the lifecycle — a session remains formally open until at least one participant asks for closure — and it prevents the arbiter from overreaching into sessions whose course it observes but has not been asked to rule on.

The numeric result is carried as tags because it is structured, per-player data that consumers extract without parsing the content. Note that relays index only single-letter tag names for filtering (NIP-01), so result tags are read client-side after fetching (by #e/#p), not used as relay-side filters. The termination status, by contrast, is a single opaque token and is therefore carried as the content payload rather than as a tag.

This NIP fixes the format and the protocol-level guarantees of the verdict (idempotence, finality). It does not define rule-system-specific evaluation logic or a fixed vocabulary of termination statuses; those remain the responsibility of the arbiter’s rule engine and application-layer conventions. Whatever the vocabulary, it MUST be decidable from published events alone: a verdict is a function of the natural state and the session’s public events, never of unpublished claims — the Adjudication Request that triggers it deliberately asserts nothing (kind 3424 §Content).

Specification

Event kind

kind: 3425

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

Operating mode

Every session has a designated arbiter (per the session’s founding context — kind 3422 §Signing party). This NIP defines the binding verdict event signed by that arbiter. The Adjudication references the session’s Game Session (kind 3422) and MUST be signed by the same pubkey that signed the Game Session — the designated arbiter.

Attestation by the designated timestamper (attested mode)

This section applies in attested mode. In self-timed mode the Adjudication’s canonical timing is its own created_at, established by acceptance at one of the session’s designated timing relays (Canonical Timing §Canonical timing of an event).

The designated timestamper publishes an Event Timestamp Attestation (kind 3410) referencing this Adjudication via the required attests-marked e tag. Its created_at is then the verdict’s canonical timing for race resolution.

The structure of the Adjudication Attestation is:

["e", "<adjudication_event_id>", "<relay_hint>", "attests"]
["e", "<game_session_event_id>", "<relay_hint>", "<app>:session"]

The second e tag carries an application-namespaced discovery marker <app>:session referencing the Game Session (the reference timestamper uses sashite:session), following the same discovery pattern as per-Ply attestations — and, as there, the designated timestamper MUST include it (kind 3423 §Time accounting). NIP-26-delegated signatures from the timestamper are NOT accepted as authoritative.

In attested mode, the Adjudication’s own created_at is informational and MUST NOT be used for any timing-dependent decision; in self-timed mode it IS the canonical timing, made non-backdatable by the designated timing relay. See Canonical Timing.

Natural state of events at adjudication

When signing an Adjudication, the arbiter MUST rule on the natural state of events at the moment given by the cutoff: the triggering Adjudication Request’s (kind 3424) canonical timing.

Specifically, the natural state consists of the longest consecutive chain of Plies following the session’s play order from its first half-move, where the Ply at each position satisfies all of the following:

  1. The Ply occupies the next slot in the play order. A Ply’s slot is (session, signer, step), where step is the signer’s own move ordinal (kind 3423 §Step semantics and play order); the mapping from slots to the play order — where each player’s step k falls — is defined by the rule system for the session’s (game, variant). For many games the two players’ sequences strictly alternate within each step value, but a rule system MAY define a different interleaving, including turn structures where a player moves more than once in a row.
  2. The Ply is canonical for its slot per Canonical Timing §Meta-resolution (or per the rule system’s selection among a slot’s candidates, where it defines one — kind 3423 §Multiple Plies for the same step).
  3. The Ply’s canonical timing is less than or equal to the cutoff (defined above).
  4. The Ply’s canonical timing is greater than or equal to t₀ (kind 3422 §Canonical session start: the later of the Game Session’s start_at tag, when present, and its canonical timing). A Ply whose canonical timing precedes t₀ is invalid (kind 3423 §Time accounting: no Ply is valid before t₀) and MUST NOT enter the natural state.

The chain stops at the first position in the play order for which no Ply satisfying all four conditions exists. Plies timed after the cutoff — canonical timing strictly greater than the cutoff — are excluded from the natural state. They MAY exist on Nostr and MAY become relevant for a future Adjudication triggered by a different Request, but they do not contribute to this verdict.

Implications

When the Adjudication Request has no canonical timing yet

If the referenced Adjudication Request has no canonical timing yet — in attested mode, not yet attested by the designated timestamper; in self-timed mode, not yet accepted by a designated timing relay — the cutoff is undefined and the arbiter cannot compute the natural state. The arbiter MUST wait for the Request to acquire canonical timing before publishing an Adjudication. The same condition binds readers: an Adjudication whose triggered_by Request has no canonical timing has no computable cutoff, and verifiers MUST treat such a verdict as pending — not yet effective as the session’s outcome — until the Request acquires one (constraint 3; Canonical Timing §The pending state).

Tags

Reference tags

Tag Cardinality Value Description
e game_session exactly one event id, optional relay hint, marker reference to the Game Session (kind 3422) for this session
e triggered_by exactly one event id, optional relay hint, marker reference to the Adjudication Request that triggered this verdict

The e tags MUST be differentiated by markers as the fourth element:

The relay hint (third element) is OPTIONAL but SHOULD be present.

Players

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

The two p tags identify the two session players, each with the marker player as its fourth element:

The set of player pubkeys MUST match the players (the player-marked p tags) in the referenced Game Session (kind 3422). The p tags serve a dual purpose: semantic (identifying the two players) and structural (notification routing via standard Nostr #p filters).

Seats

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

The Adjudication MUST carry exactly one seat tag per player:

The seat-names are the protocol’s two values, first and second (kind 3420 §Match-terms tags), one per player. The set of seat tags MUST mirror those of the referenced Game Session: same player→seat mapping.

Outcome tags

Tag Cardinality Value Description
result exactly two pubkey + integer result assigned to a player

A result tag is structured ["result", "<pubkey>", "<integer>"]:

^(100|[1-9]?[0-9])$

This regex admits values from 0 through 99 (no leading zeros) and exactly 100. Values such as 00, 001, 101, or negative values are invalid.

The two result tags MUST reference the two different players. Their integer values MUST sum exactly to 100.

Common configurations:

Content

The content field MUST contain a termination status string identifying the cause of session termination. The arbiter selects the appropriate string from the vocabulary defined by the application layer (e.g., the arbiter’s published rule system or a suite-level support document).

The content field MUST satisfy:

^[a-z]{1,32}$

That is: one to thirty-two lowercase ASCII letters (az), with no digits, no uppercase, no separators, no surrounding whitespace, and no JSON wrapping.

The content field MUST NOT carry any other data. Free-form explanation by the arbiter is not part of this NIP; if the arbiter wishes to publish commentary on the ruling, they SHOULD use a separate event (e.g., a kind 1 note) referencing this Adjudication via an e tag.

Semantic constraints

A conforming Adjudication event MUST satisfy all of the following:

  1. The event contains exactly one game_session-marked e tag and exactly one triggered_by-marked e tag.
  2. The game_session e tag references a retrievable, signature-valid, conforming Game Session event (kind 3422, per its §Semantic constraints) that is the canonical Game Session for its founding (kind 3422 §Idempotence and race resolution).
  3. The triggered_by e tag references a retrievable, signature-valid, conforming Adjudication Request (kind 3424, per its §Semantic constraints) whose game_session-marked e tag references the same Game Session as the verdict’s game_session tag, and that Request has canonical timing — the natural-state cutoff (§Natural state of events at adjudication). A verdict whose Request is untimed is pending, not effective.
  4. The event’s pubkey (the arbiter) MUST match the pubkey of the referenced Game Session — i.e., the same arbiter who signed it.
  5. The event contains exactly two p tags, each marked player, with distinct pubkeys.
  6. The pubkeys of the p tags MUST match those of the player-marked p tags in the referenced Game Session. The event contains exactly one seat tag per p tag, with pubkey matching one of the players and seat-name first or second (one each). The seat assignments MUST mirror the seat tags of the referenced Game Session.
  7. The event contains exactly two result tags. Their second elements are the two different players’ pubkeys (as identified in the p tags). Their third elements are non-negative integers matching ^(100|[1-9]?[0-9])$, summing exactly to 100.
  8. The content field is a non-empty string of one to thirty-two lowercase ASCII letters matching ^[a-z]{1,32}$, with no surrounding whitespace and no additional data.

Constraints 1, 5, 7, and 8 are checkable from the event alone. Constraints 2–4 and 6 require cross-event validation against the referenced Game Session (and, for constraint 3, the referenced Adjudication Request).

Validation responsibility

Validation of an Adjudication against the semantic constraints (§Semantic constraints) is the responsibility of the client and any participants of the session.

An Adjudication that fails any constraint is invalid and does not terminate the session. Participants MAY publish a new Adjudication Request, prompting the arbiter to publish a conforming verdict.

The arbiter is trusted to apply the rule system’s logic correctly. Disputes about whether the arbiter’s verdict reflects correct rule-system evaluation are out of scope for this protocol; the fuller trust model — arbiter discretion, the reputation layer, player responsibilities — belongs to the consuming application’s documentation (this suite’s application layer maintains one).

Idempotence and finality

A given session has exactly one canonical Adjudication — a per-session slot: because every conforming Adjudication must reference the session’s canonical Game Session (constraint 2), all of a session’s conforming verdicts share one game_session reference, and competing verdicts land in one slot. 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 Adjudications MUST be ignored by clients as the session’s outcome. They are not retracted from relays; they are simply not effective. They are not thereby void as evidence: any conforming, signature-valid Adjudication of a session witnesses that the session is over, canonical or not — which is why a Rematch Offer (kind 3430) may name any of them in its concluded_by reference, while the outcome it implies remains the canonical Adjudication’s alone (kind 3430 §Tags). In attested mode, the Adjudication’s own created_at MUST NOT be used for canonicalization; in self-timed mode it IS the canonical timing.

Once a canonical Adjudication is established (i.e., the arbiter has signed an Adjudication AND it has canonical timing — attested by the designated timestamper in attested mode, or accepted by a designated timing relay in self-timed mode), the session is terminated. No further events — including subsequent Plies, Adjudication Requests, or revised Adjudications — can alter the outcome. Until the Adjudication has canonical timing, it is in a pending state and the session is not yet formally terminated.

Examples

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

Example 1 — Decisive verdict

Bob (seat second) wins. The arbiter Carol publishes:

{
 "kind": 3425,
 "pubkey": "<carol_pubkey>",
 "created_at": 1700005100,
 "tags": [
  ["e", "<game_session_event_id>", "wss://relay.example.com", "game_session"],
  ["e", "<adjudication_request_event_id>", "wss://relay.example.com", "triggered_by"],
  ["p", "<alice_pubkey>", "wss://relay.example.com", "player"],
  ["p", "<bob_pubkey>", "wss://relay.example.com", "player"],
  ["seat", "<alice_pubkey>", "first"],
  ["seat", "<bob_pubkey>", "second"],
  ["result", "<alice_pubkey>", "0"],
  ["result", "<bob_pubkey>", "100"]
 ],
 "content": "checkmate",
 "id": "...",
 "sig": "..."
}

Example 2 — Draw

The arbiter rules a draw:

{
 "kind": 3425,
 "pubkey": "<carol_pubkey>",
 "created_at": 1700005100,
 "tags": [
  ["e", "<game_session_event_id>", "wss://relay.example.com", "game_session"],
  ["e", "<adjudication_request_event_id>", "wss://relay.example.com", "triggered_by"],
  ["p", "<alice_pubkey>", "wss://relay.example.com", "player"],
  ["p", "<bob_pubkey>", "wss://relay.example.com", "player"],
  ["seat", "<alice_pubkey>", "first"],
  ["seat", "<bob_pubkey>", "second"],
  ["result", "<alice_pubkey>", "50"],
  ["result", "<bob_pubkey>", "50"]
 ],
 "content": "repetition",
 "id": "...",
 "sig": "..."
}

Client guidelines

Clients implementing kind: 3425 SHOULD:

  1. Subscribe to kind 3425 events with a #p filter on the user’s pubkey to receive verdicts on the user’s own sessions.
  2. Subscribe to kind 3425 events with an #e filter on Game Session event IDs to follow verdicts on sessions of interest.
  3. Validate received Adjudication events against all semantic constraints (§Semantic constraints) before treating them as binding.
  4. Apply the canonical resolution rule (§Idempotence and finality) when multiple Adjudications reference the same session.
  5. Treat a canonical Adjudication as final; ignore any subsequent events (Plies, Adjudication Requests, alternative Adjudications) for the same session.
  6. Read user-visible session results from the result tags, not the content field: the content carries the cause of termination; the result tags carry the outcome distribution. Relays index only single-letter tag names (NIP-01), so result is not usable as a relay-side filter — fetch Adjudications by #e (session) or #p (player) and extract the outcome client-side.

Security considerations

References