- Sashité for Developers
- Nostr
- NIPs
- 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:
- Closes the session.
- Distributes a quantified outcome between the two players (typically
100 / 0,0 / 100, or50 / 50). - Identifies the termination cause via an open-vocabulary status string carried in
content.
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:
- The Ply occupies the next slot in the play order. A Ply’s slot is
(session, signer, step), wherestepis the signer’s own move ordinal (kind3423§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. - 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). - The Ply’s canonical timing is less than or equal to the cutoff (defined above).
- The Ply’s canonical timing is greater than or equal to t₀ (kind
3422§Canonical session start: the later of the Game Session’sstart_attag, when present, and its canonical timing). A Ply whose canonical timing precedes t₀ is invalid (kind3423§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
- No post-invocation racing: a player who attempts to “race” the arbiter by playing additional moves after invoking an Adjudication Request cannot influence the verdict with those moves; only Plies whose canonical timing is at or before the Request’s are considered.
- Future-slot Plies activate by chain progression, not by signing time: a Ply timed for a slot the play order has not yet reached is excluded from the natural state until every prior position is part of the chain. A Ply signed ahead of its turn therefore “activates” only when its prerequisite positions are themselves in the chain.
- No slot can be usurped: a Ply’s slot includes its signer, so no player can fill the other player’s position in the play order. Extra Plies signed out of order are either future-slot Plies (previous point) or competing candidates for the signer’s own slot (resolved per condition 2); neither can disrupt the opponent’s chain progression.
- Application-layer interpretation: the verdict’s content (the termination status) and result tags MUST reflect the natural state as defined above. Application-layer status vocabularies (e.g., the reference rule system’s Statuses — Sanki) interpret implicit termination conventions on this natural state, not on the state at the time the arbiter signs.
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:
["e", "<game_session_event_id>", "<relay_hint>", "game_session"]— the Game Session event (kind3422) for this session. Exactly one such tag is required.["e", "<trigger_event_id>", "<relay_hint>", "triggered_by"]— the Adjudication Request (kind3424) that triggered this verdict. Exactly one such tag is required. The cutoff for the natural state (§Natural state of events at adjudication) is that Request’s canonical timing.
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:
["p", "<player_pubkey>", "<relay_hint>", "player"]
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:
["seat", "<player_pubkey>", "<seat_name>"]
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>"]:
- The second element is the pubkey of one of the two players of the session.
- The third element is a non-negative integer matching the regular expression:
^(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:
100 / 0— decisive (one player wins, the other loses).50 / 50— draw.- Other integer combinations summing to
100are permitted but uncommon in classical chess-family rules.
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 (a–z), 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:
- The event contains exactly one
game_session-markedetag and exactly onetriggered_by-markedetag. - The
game_sessionetag references a retrievable, signature-valid, conforming Game Session event (kind3422, per its §Semantic constraints) that is the canonical Game Session for its founding (kind3422§Idempotence and race resolution). - The
triggered_byetag references a retrievable, signature-valid, conforming Adjudication Request (kind3424, per its §Semantic constraints) whosegame_session-markedetag references the same Game Session as the verdict’sgame_sessiontag, 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. - The event’s
pubkey(the arbiter) MUST match thepubkeyof the referenced Game Session — i.e., the same arbiter who signed it. - The event contains exactly two
ptags, each markedplayer, with distinct pubkeys. - The pubkeys of the
ptags MUST match those of theplayer-markedptags in the referenced Game Session. The event contains exactly oneseattag perptag, with pubkey matching one of the players and seat-namefirstorsecond(one each). Theseatassignments MUST mirror theseattags of the referenced Game Session. - The event contains exactly two
resulttags. Their second elements are the two different players’ pubkeys (as identified in theptags). Their third elements are non-negative integers matching^(100|[1-9]?[0-9])$, summing exactly to100. - The
contentfield 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:
- Subscribe to kind
3425events with a#pfilter on the user’s pubkey to receive verdicts on the user’s own sessions. - Subscribe to kind
3425events with an#efilter on Game Session event IDs to follow verdicts on sessions of interest. - Validate received Adjudication events against all semantic constraints (§Semantic constraints) before treating them as binding.
- Apply the canonical resolution rule (§Idempotence and finality) when multiple Adjudications reference the same session.
- Treat a canonical Adjudication as final; ignore any subsequent events (Plies, Adjudication Requests, alternative Adjudications) for the same session.
- Read user-visible session results from the
resulttags, not thecontentfield: thecontentcarries the cause of termination; theresulttags carry the outcome distribution. Relays index only single-letter tag names (NIP-01), soresultis not usable as a relay-side filter — fetch Adjudications by#e(session) or#p(player) and extract the outcome client-side.
Security considerations
-
Signatures: Adjudication events are signed by the designated arbiter. Receiving clients MUST verify signatures per NIP-01 before treating the verdict as binding.
-
Cross-event validation: An Adjudication is meaningful only in conjunction with the referenced Game Session and triggering Adjudication Request. Clients MUST retrieve and validate both references.
-
Arbiter trust model: The arbiter is a designated trusted third party. The protocol cryptographically constrains some adversarial behaviors (retroactive modification, impersonation, pre-creation timestamps, ruling on unrelated sessions) but does not prevent others (incorrect rule application, biased verdicts, selective non-action, attestation delay, collusion, unavailability or key compromise). The full enumeration, along with recommended player responsibilities and the role of reputation as a complementary layer, belongs to the consuming application’s documentation (this suite’s application layer maintains one).
-
Idempotence: A session has exactly one canonical Adjudication. Multiple Adjudications signed by the arbiter are resolved deterministically (§Idempotence and finality). Non-canonical Adjudications are ignored, not retracted.
-
Finality: A canonical Adjudication terminates the session irreversibly. Clients MUST NOT process Plies, Adjudication Requests, or alternative Adjudications for a session that has a canonical Adjudication.
-
Time accounting: The arbiter’s time-control accounting is based on the canonical timing of events (Canonical Timing): in attested mode, the Event Timestamp Attestations (kind
3410) signed by the designated timestamper (the events’ owncreated_atbeing informational); in self-timed mode, the events’ owncreated_at, made non-backdatable by the designated timing relay. -
Content validation: The
contentfield is normative — clients MUST validate it against^[a-z]{1,32}$. Any content failing this pattern renders the event invalid as an Adjudication. Clients MUST reject such events. -
Delegated signatures (NIP-26): NIP-26-delegated signatures from the designated arbiter are NOT accepted as authoritative for Adjudications. Authoritative signatures MUST come directly from the
pubkeyof the referenced Game Session (kind3422). See kind3420§Security considerations for the suite’s general delegation policy.
References
- NIP-01 — Basic protocol flow description
- Kind
3422— Game Session (the session being adjudicated) - Kind
3424— Adjudication Request (the triggering event) - Kind
3426— Elo Rating Attestation — generic Elo signed-snapshot format that a rating authority MAY use to publish Elo ratings derived from this Adjudication - Kind
3427— Glicko-2 Rating Attestation — generic Glicko-2 signed-snapshot format that a rating authority MAY use to publish Glicko-2 ratings derived from this Adjudication - Event Timestamp Attestation — primitive for the Adjudication’s attestation (attested mode)
- Kind
3430— Rematch Offer (references an Adjudication of the concluded session, markedconcluded_by, as its structural proof that the session is over) - Canonical Timing — canonical timing, the pending state, meta-resolution
