Nostr directory
Publishing to Nostr is optional. The envelope is self-contained and can travel over any transport (URL fragment, email, IPFS, QR code, paper, git). Nostr is one choice among many — a good one for durable public discovery.
Event kind
Kind 30083. Parameterized-replaceable, in the OrangeCheck family's
30078–30099 range.
The family's claims so far:
| Kind | Claimed by |
|---|---|
| 30078 | OrangeCheck attestations + OC Lock device records |
| 30080 | OC Vote poll |
| 30081 | OC Vote ballot |
| 30082 | OC Vote reveal |
| 30083 | OC Stamp envelope |
We deliberately did NOT reuse 30078. Relays that index by (kind, d-tag) as a
compound key would see prefix collisions across protocols over time. Clean
namespace per protocol is the rule.
Event shape
event.kind = 30083
event.tags = [
["d", "oc-stamp:" || id],
["addr", signer.address],
["hash", content.hash],
["signed_at", signed_at]
]
event.content = <canonical JSON of envelope>
event.pubkey = <ephemeral Nostr pubkey, any key>
event.created_at = unix_seconds
The Nostr event pubkey has no relationship to the Bitcoin identity. The
authenticity of the stamp is proved by the BIP-322 signature inside the
envelope, not by the Nostr author. A fresh ephemeral Nostr keypair SHOULD be
derived per-publish:
nostr_sk := HKDF(ikm=random(32), salt="oc-stamp/v1/nostr-key", info=id, L=32)
This avoids asking wallets to hold Nostr keys.
Discovery
By content hash
REQ { "kinds": [30083], "#hash": ["sha256:<hex>"] }
Use case: "has anyone stamped these bytes?"
By signer address
REQ { "kinds": [30083], "#addr": ["bc1q…"] }
Use case: "all stamps by this signer."
By envelope id
REQ { "kinds": [30083], "#d": ["oc-stamp:<id>"] }
Use case: "fetch this specific stamp."
Addressability + upgrades
Kind-30083 is addressable — relays replace prior events with the same
(pubkey, kind, d-tag) tuple.
For OC Stamp this is harmless in practice:
- The d-tag is content-addressed (
oc-stamp:<id>whereidis a hash of the canonical message). - A "replacement" with the same d-tag must carry the same id.
- Same id → same signed content. Any attempt to swap in different content fails verification on the consumer side.
The common reason to republish with the same d-tag is an OTS upgrade:
pending → confirmed. Verifiers that see multiple versions of the same id SHOULD
accept the one with ots.status === "confirmed" and the latest upgraded_at.
Recommended relays
The reference clients use this set:
wss://relay.damus.iowss://relay.nostr.bandwss://nos.lolwss://relay.snort.social
Clients SHOULD publish to at least three diverse relays. A hostile relay set can refuse to publish or return your envelope, but cannot forge it — BIP-322 verification is relay-independent.
Why Nostr is optional
Envelopes are self-contained JSON. Put them:
- In a URL fragment (
stamp.ochk.io/v#<base64url>). - Alongside the content (
blogpost.md+blogpost.md.stamp). - In IPFS / Arweave / any CAS.
- In a git repo (
.git/stamps/directory). - On paper, via QR code.
- In a Nostr event (this page).
No single directory is load-bearing. The ochk.io/stamp.ochk.io operators do not run a privileged directory — the reference aggregator uses the same Nostr relays anyone else would.