Sashité for Developers
  1. Sashité for Developers
  2. Nostr
  3. NIPs
  4. Canonical Timing

NIP-XX

Canonical Timing

draft optional

This NIP defines canonical timing: a deterministic rule by which consumers assign an authoritative timestamp to events whose author-declared created_at cannot be taken at face value. It is the timing foundation for the turn-based game-session kinds (3418–3425) and for any other kind that declares its timing “canonical per this NIP” (e.g. kind 3441). It composes two primitives defined elsewhere: the Event Timestamp Attestation (kind 3410) and the Self-Timed Timing Relay profile.

Motivation

An event’s created_at is set by its author, who may have an incentive to misrepresent it. Applications where per-event timing is decisive — clocks, deadlines, move order — need a timestamp that every consumer derives identically from public data. This NIP fixes that derivation once, so that the consuming kinds can share one rule instead of each defining (and drifting on) their own.

Timing modes and mode selection

A founding event (a kind that opens a timed context — in the game suite: kinds 3418, 3420) selects the timing mode for its whole context by carrying exactly one of the following two designations:

["timing_relay", "<wss://relay.example.com>"]

A founding event carrying neither designation, or both, is non-conforming. Events that respond to or found on a founding event (in the game suite: kinds 3419, 3422) mirror the founding designations verbatim, so that every consumer can determine the mode — and the parties to trust — from any one of them.

A context has exactly one timing authority — the designated timestamper, or the designated timing relay — so that every consumer trusts one named party, and every bound the authority applies (§Bounding a signer’s candidates for one slot) is a property of the context. Designating several relays was considered and refused (2026-09-05): it would have widened availability, not safety — acceptance at any one would have established timing, so each would have had to deserve the consumers’ trust alone — and it would have multiplied the per-slot ceiling by the number of relays.

Canonical timing of an event

Attested mode. The canonical timing of an event is the created_at of the designated timestamper’s canonical attestation of that event — per the Event Timestamp Attestation NIP §Same-signer equivocation: the attestation with the smallest created_at, tie-broken by the smallest attestation id. An event none of whose attestations by the designated timestamper are known has no canonical timing.

Self-timed mode. The canonical timing of an event is the event’s own created_at, once its acceptance by the designated timing relay is established. A consumer establishes acceptance by retrieving the event from the designated relay: a conforming relay serves only what it accepted, and its ingestion window bounds how far the created_at can deviate from the moment of acceptance. An event that cannot be retrieved from the designated relay has no canonical timing for that consumer.

Undesignated events. A kind may adopt this NIP’s self-timed rule without a founding designation (e.g. kind 3441, whose events belong to no session). Its canonical timing is the event’s own created_at, once acceptance by a profile-conforming relay that the consumer trusts is established. The choice of trusted timing relays is then explicit consumer configuration, exactly as the choice of trusted signers is elsewhere.

The pending state

An event that has no canonical timing is pending. A pending event is not part of any canonical history:

Pending is observer-relative by construction — a consumer who cannot reach the designated relay establishes nothing — and this NIP makes that relativity explicit and bounded: it never yields two different timings for one event, only “this timing” or “not established”. A pending event may leave the state at any moment (the attestation arrives; the designated relay becomes reachable); it never changes timing by doing so.

Meta-resolution: competing events for one slot

Where a consuming kind defines a slot — a position that exactly one event may canonically occupy (one Game Session per founding, one Conclusion per session, one verdict per validator and subject) — and several conforming candidates compete, the canonical occupant is:

the candidate with the smallest canonical timing, tie-broken by the smallest event id (lexicographic, lowercase hex),

evaluated over the candidates known to the consumer whose canonical timing is established. A pending candidate cannot win a slot. If no candidate’s canonical timing can be established, the slot is unresolved and the parties’ competing claims are void for that consumer.

Under this rule, in either mode, a candidate published later cannot displace an established earlier one — later publication means larger canonical timing — so slots are stable in the nominal regime, and displacing one retroactively requires both a misbehaving signer and a colluding (or non-conforming) timing infrastructure that the consumer nonetheless trusts.

Bounding a signer’s candidates for one slot

Where a consuming kind lets one signer publish several events for one slot it owns — in the game suite, the Plies of kind 3423, whose slot is (session, signer, step) and among whose candidates the session’s rule system selects — the timing authority bounds, at the source, how many of them obtain canonical timing: it confers canonical timing on at most M events of one signer for one slot, the first M it receives, and on no further one. In attested mode the designated timestamper attests no further event of that signer for that slot; in self-timed mode the designated timing relay refuses it at ingestion (Self-Timed Timing Relay §Per-slot ceiling), so it is stored nowhere it could be retrieved from. An event beyond the ceiling is therefore pending for every consumer alike (§The pending state): it is part of no canonical history, does not exist for the consuming kind’s rules, and cannot be brought into existence by publishing it elsewhere.

M is a constant of the deployment, stated by the consuming kind’s suite — for the game suite, M = 32 (max_plies_per_slot in its registry), four times the selection cap the reference rule system reads, so that the ceiling never decides a slot the rule system would have filled. Its purpose is to bound the work every consumer performs on a slot — the request a rule system evaluates is bounded by it — without any consumer-side truncation that two consumers could apply differently. Because a context has exactly one timing authority (§Timing modes and mode selection), the ceiling is a property of the context, not of a party. Which of a slot’s timed events is canonical remains the consuming kind’s rule (kind 3423 §Multiple Plies for the same step), never this NIP’s.

t₀ — the start of a timed context

Where a consuming kind anchors clocks at a context start (in the game suite: the Game Session, kind 3422), t₀ is:

the later of the context’s start_at tag value (when present) and the context event’s own canonical timing.

Consequences: with no start_at, t₀ is simply the context’s canonical timing; with a future start_at, the context is scheduled and no time is charged before it; if canonical timing is established only after start_at has passed, t₀ is that later moment — no participant is ever charged for an interval during which no valid action existed. No wall-clock observer is involved: an action event is early or late by comparing its own canonical timing to t₀ and to the budgets derived from it.

Security considerations

References