Skip to main content

Module discovery

Module discovery 

Source
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§

DiscoveryRecordV1
A signed claim that a username points at an address.
DiscoveryTombstoneV1
A tombstone: the body a retraction publishes in place of a claim.

Enums§

DiscoveryBody
What a resolved record body turned out to be.
DiscoveryRejection
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.