Chapter 03 - Timestamping
03
Nostr Implementation Possibility - Hex № 03

OpenTimestamps Attestations for Events

Attach a Bitcoin-anchored OpenTimestamps proof to any event, to show it existed before a certain time. Deprecated — read the warning below first.

Deprecated

1 Overview

Sometimes a client needs to prove that an event existed at or before a certain time, without trusting any single server to say so. OpenTimestamps solves this problem outside Nostr. It hashes a piece of data, submits the hash to calendar servers, and anchors it into a Bitcoin block through a Merkle tree. Once a Bitcoin block confirms the anchor, no one can move the timestamp earlier or later.

This NIP defines a way to carry an OpenTimestamps proof inside Nostr. A new event, kind 1040, wraps the raw bytes of an .ots proof file and points at the event it timestamps with an NIP-01 e tag. Anyone who has the target event and the kind-1040 attestation can verify, offline, that the target event existed before the Bitcoin block referenced in the proof.

The attestation is a separate event from the one it proves. A client publishes the target event first, waits for OpenTimestamps to produce a confirmed proof, then publishes the kind-1040 event that references it.

Deprecated

The Nostr NIPs project marks NIP-03 unrecommended. The source specification gives the reason as: vulnerable to one specific attack, and it needs an update. No replacement NIP is proposed as of this writing. Do not build new trust decisions on kind-1040 attestations until this NIP is revised.

Tech Note

The source markdown does not name the attack, describe its mechanism, or give a timeline for a fix. This manual restates the warning as written. Treat any kind-1040 attestation you encounter as informational only, not as cryptographic proof, until an updated specification exists.

2 The Attestation Event

A kind-1040 event follows the standard NIP-01 event envelope. This NIP defines the meaning of its tags and content fields only.

Wire format - kind 1040 attestation (corrected)
{
  "id":         <32-byte lowercase hex sha256 of the serialized event>
  "pubkey":     <32-byte lowercase hex public key of the creator>
  "created_at": <unix timestamp, seconds>
  "kind":       1040,
  "tags": [
    ["e", <target-event-id>, <relay-url>],
    ["k", <target-event-kind, as a string>]
  ],
  "content": <base64-encoded .ots file bytes>,
  "sig":        <64-byte lowercase hex Schnorr signature of the id>
}
Tech Note

The JSON example in the source markdown omits the comma after "kind": 1040, which makes it invalid JSON. The block above corrects it and fills in the standard NIP-01 fields the source example leaves out.

Kind 1040 falls in the regular range defined by NIP-01 (1000-9999). A relay is expected to store every kind-1040 event it accepts, not just the latest one.

Tags defined by this NIP
TagFull formRule
e["e", <target-event-id>, <relay-url>]Required. Points at the event this attestation proves. The id in the referenced OpenTimestamps proof must match this event id exactly.
k["k", "<target-event-kind>"]Required. The kind number of the target event, written as a string, so a client can filter attestations by the kind they cover.
Content field
FieldTypeRule
contentbase64 stringThe full, unmodified bytes of an .ots proof file, base64-encoded.

Three normative rules govern the proof itself:

  • The OpenTimestamps proof must prove the event id in the e tag as its digest — the proof is worthless if it timestamps a different piece of data.
  • The content must be the complete content of an .ots file, and that file must contain at least one Bitcoin attestation.
  • The .ots file should contain a single Bitcoin attestation. One valid attestation is enough to prove the timestamp, and a smaller file costs less to transmit and store. The file should not reference "pending" attestations — an attestation that is not yet confirmed on a Bitcoin block proves nothing, so it only adds useless bytes.
Event signed, has id OpenTimestamps digest = event id Bitcoin block confirms kind:1040 e + k tags, .ots Relay EVENT
Fig. 3-1 — Attestation pipeline. OpenTimestamps anchors the event id into a Bitcoin block; the proof is then wrapped in a kind:1040 event and published like any other event.

3 Worked Example

First, a client publishes the target event and gets an OpenTimestamps proof for it (the details of talking to calendar servers are outside this NIP). Then it publishes the kind-1040 attestation.

Example event - kind 1040 attestation
{
  "id": "7f0f4d1a…c92be3",
  "pubkey": "22a12a12…ecf19a",
  "created_at": 1749849000,
  "kind": 1040,
  "tags": [
    ["e", "e71c6ea722987debdb60f81f9ea4f604b5ac0664120dd64fb9d23abc4ec7c323", "wss://nostr-pub.wellorder.net"],
    ["k", "1"]
  ],
  "content": "AE9PU1BST0YAAP42a3zoMdV…3h5ZzE=",
  "sig": "9c1b0a3e…88f0d1"
}

Later, any client can fetch the target event, fetch this attestation, and verify the proof offline with an OpenTimestamps-aware tool. Using nak, jq, and ots:

Terminal - verify an OpenTimestamps proof
~> nak req -i e71c6ea722987debdb60f81f9ea4f604b5ac0664120dd64fb9d23abc4ec7c323 wss://nostr-pub.wellorder.net | jq -r .content | ots verify
> using an esplora server at https://blockstream.info/api
- sequence ending on block 810391 is valid
timestamp validated at block [810391]

The nak req command fetches the kind-1040 event by its id. jq -r .content pulls out the base64 .ots bytes. ots verify decodes them and checks the Bitcoin block, confirming the target event existed at or before block 810391.

4 Version History

Notable changes, newest first:

  • 20262026-05Marked unrecommended and added the vulnerability warning to the NIP body.
  • 20252025-08Fixed a missing k tag in an example, as part of a wider tag-consistency pass across several NIPs (#2001).
  • 20232023-11Explained why publishers should include only a single Bitcoin attestation rather than several.
  • 20232023-11Removed the NIP authors byline, part of a site-wide cleanup.
  • 20232023-10Removed a leftover, unused "block" tag from the spec.
  • 20232023-10Major rework: replaced the earlier design with today's kind:1040 event, e/k tags, and base64 .ots content (#804).
  • 20232023-01Quoted JSON field keys in the example, since unquoted keys are not valid JSON.
  • Origin2022-05-01fiatjaf migrated NIP-03 into the dedicated nostr-protocol/nips repository.

Original Proposal - Credits

Authorfiatjaf
First commit2022-05-01 — "migrate nips from main nostr repo"
Statusdraft - unrecommended - optional — deprecated, do not rely on it for new work
Source03.md on GitHub - full change history

5 See Also

  • NIP-01 — event envelope and kind storage classes; kind 1040 is a regular kind under these rules.
  • NIP-40 — expiration timestamps on an event; a different kind of time claim, set by the author instead of anchored in Bitcoin.
  • NIP-13 — proof of work; another way to attach a costly, independently checkable property to an event.