- Sashité for Developers
- Nostr
- NIPs
- 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:
- Attested mode — a
ptag with the markertimestamper(fourth element), designating the pubkey whose kind-3410attestations are authoritative for the context. - Self-timed mode — exactly one
timing_relaytag, designating the relay that enforces the Self-Timed Timing Relay profile:
["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:
- it cannot win a slot (§Meta-resolution) or a conflict, and it is not a candidate for any selection the consuming kind defines among a slot’s events;
- no deadline (
accept_untilor other) can be evaluated against it, in either direction; - a timed context cannot be terminated, concluded, or rated on the strength of pending events.
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_attag 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
- Trust is named, not removed. In attested mode consumers trust the designated timestamper; in self-timed mode they trust the designated relay to enforce the profile honestly. The designation reduces the judgment to one named party — it does not make the timing trustless.
- One authority per context. A context designates exactly one timing authority, so there is no second party whose acceptance could establish a timing the first would have refused, and the per-slot ceiling cannot be multiplied by designating more. Parties who would rather spread trust choose their contexts, not a wider designation.
- Dead designations. If the designated relay (or the designated timestamper) becomes permanently unreachable, the context’s unestablished events remain pending for late consumers. This is the intended failure mode: an unverifiable history stays unverifiable rather than being presumed.
- Double-fault bound. All retroactive-manipulation scenarios in this NIP require at least two simultaneous failures: a misbehaving signer (equivocating timestamper, backdating author) and a trusted timing party covering for it. Consumers who choose their designated parties carefully get deterministic, stable histories; consumers who do not have chosen their conclusions.
- Flooding a slot. A signer may publish many events for one slot; the per-slot ceiling (§Bounding a signer’s candidates for one slot) caps what the timing authority times, so the work of every consumer is bounded at the source and identically. A timing authority that exceeds its ceiling is misbehaving in the same sense as one that backdates: the events it times beyond
Mare canonically timed for the consumers who trust it, and the bound they rely on no longer holds.
References
- NIP-01 —
created_at, event ids - NIP-10 — marker convention for
p/etags - Event Timestamp Attestation — kind
3410, the attested-mode primitive - Self-Timed Timing Relay — the self-timed-mode relay profile
- Kinds
3418–3420,3422,3423,3425— the game-session suite consuming this NIP - Kind
3441— Validation (undesignated self-timed consumer)
