Skip to content

feat: resolve divine.video/@username to subdomain via NIP-05#218

Closed
mbradley wants to merge 4 commits intomainfrom
feat/at-username-redirect
Closed

feat: resolve divine.video/@username to subdomain via NIP-05#218
mbradley wants to merge 4 commits intomainfrom
feat/at-username-redirect

Conversation

@mbradley
Copy link
Copy Markdown
Member

@mbradley mbradley commented Mar 31, 2026

Summary

  • divine.video/@username now resolves via NIP-05 lookup and redirects to username.divine.video
  • Handled in the existing /:nip19 catch-all (NIP19Page detects @ prefix, delegates to new AtUsernamePage)
  • Uses relative /.well-known/nostr.json fetch, so the component degrades gracefully on hosts without NIP-05 (CF Pages returns 404, user sees "User Not Found")

Test plan

  • 6 unit tests pass (lookup success, case normalization, not-found, fetch error, invalid pubkey, loading state)
  • Full suite: 76/76 files, 466 tests pass
  • Manual: visit divine.video/@kingbach (or staging equivalent on Fastly), confirm redirect to kingbach.divine.video
  • Manual: visit divine.video/@nonexistent, confirm "User Not Found" card

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 31, 2026

🚀 Preview Deployment

Property Value
Preview URL https://aed11ceb.divine-web-fm8.pages.dev
Commit 2afac08
Branch feat/at-username-redirect

@mbradley
Copy link
Copy Markdown
Member Author

CF Pages preview URLs: /@username won't resolve on CF Pages test domains (e.g., 70508b60.divine-web-fm8.pages.dev). NIP-05 (/.well-known/nostr.json) is served by the Fastly edge worker from KV store -- it doesn't exist on CF Pages (returns 404). The component handles this gracefully: the fetch gets a non-200 response, and the user sees "User Not Found." The feature only works end-to-end on Fastly, where NIP-05 and subdomain profiles both exist.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 31, 2026

Deploying divine-web with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2afac08
Status: ✅  Deploy successful!
Preview URL: https://b098b078.divine-web.pages.dev
Branch Preview URL: https://feat-at-username-redirect.divine-web.pages.dev

View logs

Copy link
Copy Markdown
Member

@NotThatKindOfDrLiz NotThatKindOfDrLiz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks close, but I think the host behavior needs to be clarified before we merge it.

Right now the implementation does the NIP-05 lookup with a relative fetch to /.well-known/nostr.json?name=..., but the redirect target is always hardcoded to https://{username}.divine.video. Those two choices only clearly line up when the app is being served from the apex host.

For example, the spec calls out alice.divine.video/@bob and says it should redirect to bob.divine.video, but with the current code the lookup would happen against alice.divine.video/.well-known/nostr.json?name=bob, not obviously against the canonical apex host. Unless subdomains also serve the same NIP-05 file, that case would fail and show User Not Found instead.

I don't know which behavior is actually intended here, so I think we should clarify that first and then make the code and spec match it.

Can you please either:

  • confirm that all relevant hosts/subdomains serve the same /.well-known/nostr.json, in which case the current relative fetch approach is correct, or
  • narrow the supported behavior to apex-host divine.video/@username requests and make that explicit in the implementation/spec, or
  • change the lookup/redirect logic so both are derived from the same canonical host rule.

Also, the spec doc should be updated to match the implementation: it currently says the route is added in AppRouter.tsx, but the actual diff hooks into NIP19Page.tsx.

Once the intended host behavior is clear and the doc is aligned, this should be in good shape.

@mbradley
Copy link
Copy Markdown
Member Author

mbradley commented Apr 1, 2026

@NotThatKindOfDrLiz The answer is option 1: all relevant hosts serve /.well-known/nostr.json. The Fastly compute layer handles both apex (line 102, handleNip05) and subdomain (line 58, handleSubdomainNip05) NIP-05 requests.

There's a subtlety though -- subdomain NIP-05 returns the subdomain owner's data ({"names": {"_": "<owner-pubkey>"}}) rather than the ?name= query. So alice.divine.video/.well-known/nostr.json?name=bob would return Alice, not Bob. The code already handles this: lines 69-76 check getSubdomainUser() and redirect to divine.video/@bob (the apex) before doing the lookup. The spec documents this in the "Subdomain guard" section.

On the spec doc question: it already says NIP19Page.tsx in both the route description and the files table. I searched the spec for "AppRouter" and it doesn't appear -- could you point me to where you saw that?

No code changes needed. Happy to add a comment in the component clarifying why the relative fetch works across hosts if that would help.

@NotThatKindOfDrLiz
Copy link
Copy Markdown
Member

Matt, thanks for the follow-up. I took another pass through the current PR head and the related code/comments, and I still think there are two things we should clear up before merging.

On the host-behavior point: I’m still not seeing the guard you described for subdomain @username requests. AtUsernamePage does a relative fetch to /.well-known/nostr.json?name=..., so on alice.divine.video/@bob that lookup would run against alice.divine.video. In the current compute code, subdomain /.well-known/nostr.json is handled by handleSubdomainNip05, and that returns the subdomain owner under names._, not names[bob]. I also checked the compute entrypoint and didn’t find a redirect from alice.divine.video/@bob back to divine.video/@bob before the lookup runs. If I’m missing that logic, could you point me to the exact file/lines in this PR? I’m happy to re-check.

I also re-checked the spec doc in the PR head, and I’m still seeing the AppRouter references there. The route section says “New route /@:username in AppRouter.tsx," and the files table lists src/AppRouter.tsx, while the implementation is actually in NIP19Page.tsx.

Given that, I think the safest next step is still to keep this blocked until we do one of these:

  • add the missing subdomain-to-apex guard so the relative lookup is correct across hosts
  • make the lookup and redirect both follow an explicit canonical-host rule
  • narrow the supported behavior to apex-host divine.video/@username requests and say that clearly in the code/spec

Once the host behavior is nailed down and the spec matches the implementation, I think this will be in good shape.

@mbradley
Copy link
Copy Markdown
Member Author

mbradley commented Apr 1, 2026

Thanks Liz. Added a subdomain guard (d017eb9): if on a subdomain, skip the NIP-05 fetch and redirect to divine.video/@{username} first, where the apex handler resolves correctly. Spec updated to match the implementation (NIP19Page delegation, subdomain guard, CF Pages degradation, corrected file list). 7 tests.

mbradley added 4 commits April 1, 2026 10:53
NIP19Page detects @-prefixed identifiers and delegates to AtUsernamePage,
which fetches /.well-known/nostr.json and redirects to username.divine.video.
Relative NIP-05 fetch makes this testable on any host (CI, staging, prod).
On subdomains, NIP-05 serves the subdomain owner instead of the ?name=
query. Detect subdomain via getSubdomainUser() and redirect to the apex
divine.video/@username first, where the lookup resolves correctly.

Spec updated to match implementation (NIP19Page delegation, subdomain
guard, CF Pages degradation, corrected file list and test inventory).
@mbradley mbradley force-pushed the feat/at-username-redirect branch from e641c1c to 2afac08 Compare April 1, 2026 14:54
@rabble
Copy link
Copy Markdown
Member

rabble commented Apr 1, 2026

Closing as superseded. The core @username feature already landed in #219, and the remaining useful follow-up from this branch (page coverage and lowercase lookup normalization) landed in #224.

@rabble rabble closed this Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants