From fda9fb14c6498565ac13315a5c46086cf8881ba7 Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Thu, 16 Jul 2026 19:56:57 +0300 Subject: [PATCH] fix: filter NIP-05 nostr.json by the ?name query param Serve /.well-known/nostr.json from a dynamic route handler instead of a static public/ file. A static file can't read the query string, so every handle was returned on every request. The handler honors the NIP-05 `?name=` query and returns only the requested mapping, or `{"names":{}}` when the name is absent or unknown. CORS moves into the handler so the endpoint owns its whole response; keeping the header in next.config too would emit it twice. Co-Authored-By: Claude Opus 4.8 --- app/.well-known/nostr.json/route.js | 32 +++++++++++++++++++++++++++++ next.config.mjs | 12 ++--------- public/.well-known/nostr.json | 6 ------ 3 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 app/.well-known/nostr.json/route.js delete mode 100644 public/.well-known/nostr.json diff --git a/app/.well-known/nostr.json/route.js b/app/.well-known/nostr.json/route.js new file mode 100644 index 0000000..f2b380a --- /dev/null +++ b/app/.well-known/nostr.json/route.js @@ -0,0 +1,32 @@ +// NIP-05 identifier endpoint — https://github.com/nostr-protocol/nips/blob/master/05.md +// +// Served as a dynamic route (not a static public/ file) so we honor the +// `?name=` query and return only the requested mapping, instead of +// enumerating every handle on the domain. + +const PUBKEY = + '3e294d2fd339bb16a5403a86e3664947dd408c4d87a0066524f8a573ae53ca8e' + +// name -> hex pubkey (lowercase). `_` is the root identifier: sepehr@zignostr.com +// with no local part resolves to it per NIP-05. +const NAMES = { + _: PUBKEY, + sepehr: PUBKEY, +} + +// The response depends on the query string, so never prerender or cache it. +export const dynamic = 'force-dynamic' + +export function GET(request) { + const name = new URL(request.url).searchParams.get('name') + const pubkey = name ? NAMES[name] : undefined + + return Response.json( + { names: pubkey ? { [name]: pubkey } : {} }, + { + // NIP-05 requires the file to be readable cross-origin so web clients can + // verify name@domain -> pubkey. + headers: { 'Access-Control-Allow-Origin': '*' }, + }, + ) +} diff --git a/next.config.mjs b/next.config.mjs index d80eae5..c047ec4 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -8,14 +8,6 @@ export default withNextra({ reactStrictMode: true, // No metadataBase / hard-coded site URL here on purpose: the public domain is // configured in the deploy dashboard (Vercel), never committed to this repo. - async headers() { - return [ - { - // NIP-05 requires the identifier file to be readable cross-origin so - // web clients can verify name@domain -> pubkey mappings. - source: '/.well-known/nostr.json', - headers: [{ key: 'Access-Control-Allow-Origin', value: '*' }], - }, - ] - }, + // The NIP-05 CORS header now lives in app/.well-known/nostr.json/route.js, + // alongside the handler that builds the response. }) diff --git a/public/.well-known/nostr.json b/public/.well-known/nostr.json deleted file mode 100644 index 656b30c..0000000 --- a/public/.well-known/nostr.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "names": { - "_": "3e294d2fd339bb16a5403a86e3664947dd408c4d87a0066524f8a573ae53ca8e", - "sepehr": "3e294d2fd339bb16a5403a86e3664947dd408c4d87a0066524f8a573ae53ca8e" - } -}