- Sashité for Developers
- Nostr
- NIPs
- Elo Rating Attestation
NIP-XX
Elo Rating Attestation
draft optional
Abstract
This NIP defines an event kind for a rating authority to publish a signed attestation of an Elo rating update for a single player following the outcome of a two-player duel. The event is self-contained: it carries the player’s pre-duel and post-duel Elo ratings, the player’s score in the duel, and the K-factor used in the computation. The event does not depend on any other NIP and may be used for any two-player game whose outcome can be expressed as a score in the interval [0, 1] (with 1, 0.5, and 0 as the classical win, draw, and loss).
Motivation
The Elo rating system, devised by Arpad Elo and originally adopted by chess federations, computes a player’s skill rating from their duel outcomes against other rated players. Many two-player abstract strategy games — chess, shogi, xiangqi, go, draughts, and others — use Elo or Elo-derived systems for ranking.
A publicly signed, self-contained rating attestation event serves three purposes:
- Auditability — anyone can verify the computation against the documented Elo formula, given the inputs carried by the event.
- Discoverability — clients can query a player’s rating history without recomputing it from raw duel outcomes.
- Pluralism — multiple rating authorities (regional federations, online platforms, application-specific operators) MAY publish independent attestations for the same duels; consumers choose which authorities to trust.
The event is minimalist by design. It carries one attestation for one player, plus references to the duel and the rating pool. Verification logic, prior-rating lookups, and consumer-side filtering are left to the consuming application.
Specification
Event kind
kind: 3426
Elo Rating Attestation events are regular events per NIP-01: immutable, persistent, and referenced by their event ID.
Signer — the rating authority
The event is signed by a rating authority — a pubkey that computes and certifies the rating update. The rating authority is conceptually independent of the duel’s participants, of any arbiter or other role in the consuming application, and of any timestamping service. Multiple rating authorities MAY publish independent attestations for the same duel; consumers choose which authorities to trust on out-of-band grounds (reputation, NIP-05 verification, federation membership, etc.).
Tags
Reference tags
| Tag | Cardinality | Value | Description |
|---|---|---|---|
e duel |
exactly one | event id, optional relay hint, marker duel |
reference to the duel event (kind and structure defined by the consuming application) |
p |
exactly one | player pubkey | the player whose rating is attested |
The duel referent is application-defined at the primitive level. In this suite, the duel e tag references the Game Session (kind 3422) event id — the immutable event whose adjudicated outcome the rating consumes; both attestations of a duel (one per player) carry the same reference, which is the deduplication key below.
Rating-pool tags
| Tag | Cardinality | Value | Description |
|---|---|---|---|
game |
exactly one | game identifier matching ^[a-z][a-z0-9]{0,31}$ |
the game in which this rating is computed |
variant |
0 to 2 | variant identifier matching ^[a-z][a-z0-9]{0,31}$ |
the variant(s) of the game; cardinality interpretation below |
The variant tag cardinality conveys the variant structure of the duel:
- 0 — the game has no formal variants, or the rating applies to the base game.
- 1 — both players played the same variant; the rating belongs to the (game, variant) pool.
- 2 — the players played different variants in a multi-variant duel. The two variants are listed without prescribed order; the rating authority’s published policy determines which pool, if any, the rating contributes to (e.g. a per-game pool that unifies the game’s variants treats the duel as belonging to that single game pool, whereas a per-(game, variant) pool excludes it).
The 0–2 bound is intrinsic, not an arbitrary restriction: a rating attestation describes a duel, which is two-participant, so at most two distinct variants are present. It is the two-participant projection of the suite’s general 0–n variant convention — a kind states the general form its subject admits, and lets the subject’s own arity bound it: a kind describing a position with an unbounded number of participants would state the general form.
Rating computation tags
| Tag | Cardinality | Value | Description |
|---|---|---|---|
score |
exactly one | decimal in [0, 1] | the rated player’s score in the duel |
elo_pre |
exactly one | non-negative decimal | the player’s Elo rating before this duel |
elo_post |
exactly one | non-negative decimal | the player’s Elo rating after this duel |
k_factor |
exactly one | positive decimal | the K-factor used in the computation |
Score values
The score tag’s value MUST be a decimal number in the closed interval [0, 1], representing the rated player’s outcome in the duel. The opponent’s score is 1 − score.
The three classical values are the common cases:
1— the rated player won the duel.0.5— the duel was drawn.0— the rated player lost the duel.
Values strictly between these represent partial or asymmetric outcomes — for example a rule system that awards a partial win (a result better than a draw but short of a full win) to the player who forces a particular terminal position. Game-specific result notations (chess "1-0" / "1/2-1/2" / "0-1", go "B+R" / "W+R" / "Jigo", etc.) MUST be normalized to a value in [0, 1] when constructing the event. The Elo formula below is defined for any such score, since the actual score enters only through the term (score − expected_score).
Content
The content field MUST be the empty string (""). The semantics of the event are fully expressed by its tags.
Elo formula
For reference, the player’s post-duel rating is computed as:
elo_post = elo_pre + k_factor × (score − expected_score)
expected_score = 1 / (1 + 10^((opponent_elo_pre − elo_pre) / 400))
Where opponent_elo_pre is the opponent’s pre-duel Elo rating in the same rating pool (same game, same variant, same rating authority). The opponent’s pre-duel rating is obtained by the consumer from the rating authority’s prior Elo Rating Attestation for the opponent (or the rating authority’s documented initial-rating convention if no prior attestation exists).
Semantic constraints
Throughout these constraints, a decimal number is a string matching ^(0|[1-9][0-9]*)(\.[0-9]+)?$ — no sign, no exponent, no leading zeros, . as the separator; non-negative admits any such string, and positive additionally excludes representations of zero (0, 0.0, …).
A conforming Elo Rating Attestation event MUST satisfy all of the following:
- Exactly one
etag with markerduel, whose second element is a valid event id. - Exactly one
ptag. - Exactly one
gametag whose second element matches^[a-z][a-z0-9]{0,31}$. - Zero, one, or two
varianttags, each whose second element matches^[a-z][a-z0-9]{0,31}$. - Exactly one
scoretag whose second element is a decimal number (grammar above) in the closed interval [0, 1]. - Exactly one
elo_pretag whose second element is a non-negative decimal number (grammar above). - Exactly one
elo_posttag whose second element is a non-negative decimal number (grammar above). - Exactly one
k_factortag whose second element is a positive decimal number (grammar above). - The
contentfield is the empty string ("").
All nine constraints are checkable from the event alone. Verifying that elo_post is the correct result of the Elo formula applied to elo_pre, the opponent’s pre-duel rating, score, and k_factor requires cross-event lookups and is the consumer’s responsibility.
Race resolution
A rating authority SHOULD publish at most one Elo Rating Attestation per (rating authority, player, duel) triple. If multiple events exist for the same triple, the canonical event is determined deterministically:
- Smallest
created_atfirst. - Smallest event ID as tiebreaker.
Non-canonical events MUST be ignored by consumers.
This rule uses the event’s own created_at — the rating authority’s self-declared timestamp. The NIP does not require external timestamping. Consumers requiring authoritative, third-party-verified timing (for example, to detect a rating authority backdating their attestations) SHOULD layer an external mechanism such as the Event Timestamp Attestation NIP on top of these events.
A rating authority who realizes they computed incorrectly cannot override their first Attestation by publishing a later one (the later event would have a larger created_at and thus be non-canonical). Their options are NIP-09 deletion (with the usual caveats about relay support and asynchronous propagation) or accepting the error as historical. Signed events are immutable.
Examples
Example 1 — Standard chess duel, decisive
Alice (rated 1500) defeats Bob (rated 1500) in a chess duel. The rating authority publishes Alice’s attestation:
{
"kind": 3426,
"pubkey": "<rating_authority_pubkey>",
"created_at": 1700000000,
"tags": [
["e", "<duel_event_id>", "wss://relay.example.com", "duel"],
["p", "<alice_pubkey>"],
["game", "chess"],
["variant", "chess"],
["score", "1"],
["elo_pre", "1500"],
["elo_post", "1516"],
["k_factor", "32"]
],
"content": "",
"id": "...",
"sig": "..."
}
Verification: with K=32, equal pre-ratings give expected_score = 0.5, so elo_post = 1500 + 32 × (1 − 0.5) = 1516. ✓
A symmetric attestation is published for Bob with score = 0, elo_pre = 1500, elo_post = 1484.
Example 2 — Go duel, draw
Alice (rated 1842) draws Bob (rated 1800) in a go duel. The rating authority publishes Alice’s attestation:
{
"kind": 3426,
"pubkey": "<rating_authority_pubkey>",
"created_at": 1700000000,
"tags": [
["e", "<duel_event_id>", "wss://relay.example.com", "duel"],
["p", "<alice_pubkey>"],
["game", "go"],
["score", "0.5"],
["elo_pre", "1842"],
["elo_post", "1840.56"],
["k_factor", "24"]
],
"content": "",
"id": "...",
"sig": "..."
}
Verification: expected_score = 1 / (1 + 10^((1800 − 1842) / 400)) ≈ 0.56015, so elo_post = 1842 + 24 × (0.5 − 0.56015) ≈ 1840.56. ✓ The variant tag is absent: the rating pool is the base go game. Decimal precision is permitted in elo_post.
Example 3 — Multi-variant duel
Alice plays the chess variant, Bob plays the ogi variant, on a shared board (a multi-variant duel in a hybrid platform). The rating authority publishes Alice’s attestation, listing both variants present in the duel:
{
"kind": 3426,
"pubkey": "<rating_authority_pubkey>",
"created_at": 1700000000,
"tags": [
["e", "<duel_event_id>", "wss://relay.example.com", "duel"],
["p", "<alice_pubkey>"],
["game", "sanki"],
["variant", "chess"],
["variant", "ogi"],
["score", "0"],
["elo_pre", "1700"],
["elo_post", "1684"],
["k_factor", "32"]
],
"content": "",
"id": "...",
"sig": "..."
}
Two variant tags signal the multi-variant nature of the duel. The pool the attestation contributes to follows the rating authority’s own pool computation (§Rating pool tags): an authority computing pergame pools — the reference deployment’s choice for sanki — credits the duel to the single sanki pool, the variant tags being descriptive only. A pervariant authority would exclude the duel from its pools and publish no attestation for it.
Example 4 — Partial-win outcome
Some rule systems award a partial result that is neither a full win nor a draw. (This is a generic capability of the attestation; the reference sanki arbiter never emits one — per the reference authority’s supporting document, Rating Specification — Elo.) Here a hypothetical rule system scores Alice’s terminal position as a partial win (0.75). Both players played the same (game, variant), so the rating belongs to that single pool:
{
"kind": 3426,
"pubkey": "<rating_authority_pubkey>",
"created_at": 1700000000,
"tags": [
["e", "<duel_event_id>", "wss://relay.example.com", "duel"],
["p", "<alice_pubkey>"],
["game", "<game>"],
["variant", "<variant>"],
["score", "0.75"],
["elo_pre", "1500"],
["elo_post", "1508"],
["k_factor", "32"]
],
"content": "",
"id": "...",
"sig": "..."
}
Verification: with equal pre-ratings expected_score = 0.5, so elo_post = 1500 + 32 × (0.75 − 0.5) = 1508. ✓ Alice’s opponent receives a symmetric attestation with score = 0.25, elo_post = 1492.
Client guidelines
Clients implementing kind: 3426 SHOULD:
- Subscribe to
kind: 3426with appropriate#pfilters to track displayed players’ ratings, or withauthorsfilters when tracking specific rating authorities. - Validate received events against the semantic constraints (§Semantic constraints) before treating them as authoritative.
- Decide which rating authorities to trust based on user preferences or application policy; ratings from different authorities SHOULD NOT be averaged or combined silently.
- To determine a player’s current rating in a pool from a trusted rating authority, select the most recent Elo Rating Attestation matching the player and the pool scope in effect (where the consumer is evaluating a
ratingfilter, the scope the filter declares — kind3418§Match-terms tags): under a pervariant scope, attestations whosegameand singlevarianttag identify the (game, variant) pool; under a pergame scope (e.g., the reference deployment’ssankiauthority), all attestations carrying thatgame, regardless of theirvarianttags (zero, one, or two). “Most recent” is determined by the event’screated_at(largest first), with the smallest event ID as tiebreaker. Apply §Race resolution first — deduplicate to the canonical attestation per(authority, player, duel)triple — and select among the survivors, so a republished duplicate never masquerades as a newer rating. Thiscreated_at-based selection is sound only for an authority that publishes in duel order and never back-fills older duels (the reference authority is forward-only — Rating Specification — Elo §Signed-snapshot production) — under a back-filling authority, an attestation for an older duel published later would wrongly become “current”, and consumers of such an authority need a duel-timing-based selection instead. - When verifying a rating computation, fetch the opponent’s contemporaneous pre-duel Elo rating from the same rating authority’s prior Attestation and recompute the Elo formula.
- For a player with no Attestation from a trusted authority, treat the player as unrated. Initial-rating conventions are at the authority’s discretion.
Security considerations
- Signatures — events are signed by the rating authority. Consumers MUST verify signatures per NIP-01 before treating an Attestation as authoritative.
- Rating authority trust — the rating authority’s correctness, neutrality, and accountability are out of band for this NIP. A malicious or buggy rating authority MAY publish events inconsistent with the Elo formula. Consumers SHOULD independently verify computations where integrity is critical.
- Multi-authority divergence — different rating authorities MAY publish different attestations for the same duel. This is by design. Each authority’s perspective SHOULD be presented separately.
- Self-declared timing — the event’s
created_atis the rating authority’s self-declared timestamp; there is no in-protocol mechanism to verify that the authority did not backdate or forward-date their attestations. Race resolution (§Race resolution) and current-rating lookup (§Client guidelines, item 4) both rely on this self-declared timestamp. Consumers requiring authoritative, third-party-verified timing SHOULD layer an external mechanism such as the Event Timestamp Attestation NIP on top of these events. - Immutability — an Attestation once signed cannot be retracted other than via NIP-09 deletion requests, with the usual caveats about relay support and asynchronous propagation.
References
- Arpad E. Elo, The Rating of Chessplayers, Past and Present (1978)
- NIP-01 — Basic protocol flow
- NIP-09 — Event deletion
- The Event Timestamp Attestation NIP — generic primitive for authoritative timing, layerable on top of these events
