MVP slice of #445. Ship the smallest useful NIP-05 endpoint first; defer aggregation, relays, per-pod overrides to follow-ups.
Scope
Single-user mode only. Single name, single pubkey. No knobs.
GET /.well-known/nostr.json returns:
{ "names": { "<single-user-name>": "<hex pubkey from the profile VM>" } }
Where:
<single-user-name> = the NIP-05 reserved _ name. NIP-05 §3 specifies _ as the "naked domain" identifier — the user IS the domain, no name@ prefix needed. For a single-user pod where the pod IS the server, that's the natural identifier. --single-user-name controls the WebID path (separate concern); NIP-05 in single-user mode is intrinsically single-name and uses _. A future phase can add --nip05-name <name> if alice@example.com style is wanted on a single-user pod.
<hex pubkey> = read from /profile/card.jsonld's verificationMethod via the existing extractNostrPubkeysFromProfile helper in src/auth/nostr-keys.js. No new key-reading code, no touching /private/.
If no key is provisioned (no VM in the profile, or the VM has no Nostr-shaped pubkey), return { "names": {} }. NIP-05 verifiers handle the empty case fine.
If the server isn't in single-user mode, return 404 — multi-user aggregation is the next slice.
Public read (no WAC, no auth). Access-Control-Allow-Origin: * so browser-based Nostr clients can verify.
What this MVP doesn't ship (deferred to follow-ups)
- Multi-user aggregation (path mode) — one shared
/.well-known/nostr.json listing every pod's mapping.
- Subdomain mode per-host filtering — each subdomain serves its own.
?name= query filtering (per NIP-05 §3) — return everything for now.
relays field auto-populated from --nostr.
nip46 remote signer.
- Per-pod opt-out / opt-in.
- Custom names via
--nip05-name.
Acceptance
Refs #445.
MVP slice of #445. Ship the smallest useful NIP-05 endpoint first; defer aggregation, relays, per-pod overrides to follow-ups.
Scope
Single-user mode only. Single name, single pubkey. No knobs.
GET /.well-known/nostr.jsonreturns:{ "names": { "<single-user-name>": "<hex pubkey from the profile VM>" } }Where:
<single-user-name>= the NIP-05 reserved_name. NIP-05 §3 specifies_as the "naked domain" identifier — the user IS the domain, noname@prefix needed. For a single-user pod where the pod IS the server, that's the natural identifier.--single-user-namecontrols the WebID path (separate concern); NIP-05 in single-user mode is intrinsically single-name and uses_. A future phase can add--nip05-name <name>ifalice@example.comstyle is wanted on a single-user pod.<hex pubkey>= read from/profile/card.jsonld'sverificationMethodvia the existingextractNostrPubkeysFromProfilehelper insrc/auth/nostr-keys.js. No new key-reading code, no touching/private/.If no key is provisioned (no VM in the profile, or the VM has no Nostr-shaped pubkey), return
{ "names": {} }. NIP-05 verifiers handle the empty case fine.If the server isn't in single-user mode, return 404 — multi-user aggregation is the next slice.
Public read (no WAC, no auth).
Access-Control-Allow-Origin: *so browser-based Nostr clients can verify.What this MVP doesn't ship (deferred to follow-ups)
/.well-known/nostr.jsonlisting every pod's mapping.?name=query filtering (per NIP-05 §3) — return everything for now.relaysfield auto-populated from--nostr.nip46remote signer.--nip05-name.Acceptance
GET /.well-known/nostr.jsonon a--single-user --provision-keysserver returns{ names: { "_": "<hex>" } }.{ names: {} }.Access-Control-Allow-Origin: *header on the response./private/.Refs #445.