Expand description
DiscoveryRecordV1: a signed, non-authoritative claim that a username
points at an address.
§A claim, never a fact
Anyone may publish any username claim at the right tag. There is no
first-publisher-wins on a Nostr relay and this layer does not invent one.
Every record says exactly one thing: this key asserts this name. What a
record cannot do is lie about a key, because
derive_address(pubkey) == address is checked on every one — so the worst a
squatter achieves is to offer you a name attached to their own real identity,
which is precisely NIP-05’s threat model and precisely why its “identify,
never verify” semantics are the right ones to adopt.
The consequence is normative and it belongs at the top of this file: a
resolver MUST surface the whole set of claimants and let a human arbitrate.
A UI that silently picks the first result has converted a deliberately
non-authoritative directory into an authoritative-looking one, which is worse
than not shipping it at all. See docs/spec/username-discovery.md.
§Why a username resolves to a set, always
One record per device. Each install signs its own record with its own
identity key and publishes it under its own Nostr key, so all of a user’s
devices coexist at the same tag (addressable replacement is keyed on
(kind, pubkey, d), and each device is a different pubkey). A resolver
queries once and receives the whole set.
This is not a limitation to be designed around: no device knows the
addresses of its siblings — there is no identity export, import, backup or
sync API anywhere in this SDK, by construction — so a record shaped as
{username, [devices]} could not be produced at all. Aggregating at the tag
reaches the identical result with zero coordination. A username is therefore
1:N even for a single-device user, who is simply a set of one.
§The nostr_author binding
Step 5 of verification is what this record has and the published key-package record does not, and it closes a residual that is materially worse for a directory than for a key package.
Because the seal key is publicly derivable, a squatter can unseal a record,
re-seal the untouched (and genuinely signed) payload under their own author
key, and republish it. For a key package the cost is a dead session and one
exchange of delay. For a directory entry it defeats retraction:
addressable replacement is per-pubkey, so the owner’s tombstone replaces
only the owner’s own event and never a copy standing under someone else’s
key. That would keep a rotated-away or compromised address in the directory
indefinitely. Binding the author key inside the signed payload makes a
re-authored copy fail outright.
Structs§
- Discovery
Record V1 - A signed claim that a username points at an address.
- Discovery
Tombstone V1 - A tombstone: the body a retraction publishes in place of a claim.
Enums§
- Discovery
Body - What a resolved record body turned out to be.
- Discovery
Rejection - Why a discovery record was rejected.
Constants§
- DISCOVERY_
SIGN_ DOMAIN - Signature domain for discovery records.
- DISCOVERY_
VERSION - The only discovery record version this build produces or accepts.
Functions§
- parse_
discovery_ body - Parses a record body, distinguishing a claim from a retraction.
- verify_
discovery_ record - Verifies a discovery record against the username it was queried for and the Nostr key it was published under.