- 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, 3430) 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, 3430) 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 — one or more
timing_relaytags, each designating a 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 ratify a founding event (in the game suite: kinds 3419, 3421, 3422) mirror the founding designations verbatim, so that every consumer can determine the mode — and the parties to trust — from any one of them.
Designating several timing relays widens availability, not safety: canonical timing is established by acceptance at any one designated relay, so every designated relay must individually deserve the consumers’ trust (see §Security).
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 at least one designated timing relay is established. A consumer establishes acceptance by retrieving the event from a 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 any 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;
- no deadline (
accept_untilor other) can be evaluated against it, in either direction; - a timed context cannot be terminated, adjudicated, 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 Adjudication 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.
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 each designated relay to enforce the profile honestly. The designation reduces the judgment to named parties — it does not make the timing trustless.
- Per-relay trust. With several
timing_relaydesignations, acceptance by any one establishes canonical timing; a single dishonest or non-conforming designated relay therefore suffices to corrupt it. Parties SHOULD designate only relays they would each trust alone. - Dead designations. If every 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.
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–3425,3430— the game-session suite consuming this NIP - Kind
3441— Validation (undesignated self-timed consumer)
