Skip to content

fix(profile-urls): friendly NIP-05 /u/ paths and search card identifier#403

Open
jalcine wants to merge 4 commits into
mainfrom
fix/friendly-nip05-urls
Open

fix(profile-urls): friendly NIP-05 /u/ paths and search card identifier#403
jalcine wants to merge 4 commits into
mainfrom
fix/friendly-nip05-urls

Conversation

@jalcine

@jalcine jalcine commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Closes #402

Summary

  • /u/_%40jacky.divine.video and similar percent-encoded NIP-05 URLs now resolve, and the same profile is reachable as /u/jacky, /u/jacky.divine.video, and /u/jacky.dvine.video. Bare local part on the default apex means the default apex. Third-party NIP-05s become /u/alice.primal.net.
  • Search results card on /search?q=... shows the NIP-05 as the secondary identifier when one is set, instead of a random genUserName(pubkey) or a stale legacy Vine handle. The card link and the displayed handle now point at the same identity.

Motivation

Two layered bugs combined to make the URL in the issue fail end-to-end. First, every in-app author link was produced by buildProfileLinkPath as /u/${encodeURIComponent(nip05)}, which for _@jacky.divine.video yielded the ugly +%40... form. Second, the lookup at useUniversalUserLookup only matched the literal metadata.nip05 field on a kind-0 event (limit 500) and fell back to name@openvine.co for any input that didn't contain @, producing the misleading "Could not find a user with legacy Vine username or NIP-05 identifier" page. The search card compounded the confusion by only showing the kind-0 name field and never the NIP-05, so a user had no way to know that @Minimal Mouse 1 was the same identity as jacky.divine.video.

What changed

  • src/lib/profileLinks.ts: new toFriendlyPath(nip05) helper and new nip05CandidatesFromUrlSegment(segment) inverse. buildProfileLinkPath emits /u/jacky for _@jacky.divine.video / jacky@divine.video, /u/jacky.dvine.video for the alternate apex, /u/alice.primal.net for third-party NIP-05s, and falls back to /{npub} when the NIP-05 is missing or malformed.
  • src/pages/UniversalUserPage.tsx: useUniversalUserLookup now expands the URL segment into the candidate list via nip05CandidatesFromUrlSegment, matches kind-0 against any candidate (catching both _@x.y and x@y forms), falls back to NIP-05 DNS resolution via resolveNip05ToPubkey for candidates containing @, and drops the openvine.co legacy fallback for any subdomain-shaped input.
  • src/pages/SearchPage.tsx: UserCard now reads metadata.nip05 and shows it as the secondary line, formatted via getDivineNip05Info (@jacky.divine.video) for divine NIP-05s and via toFriendlyPath (@alice.primal.net) for third-party. Legacy metadata.name is still shown when no NIP-05 is set.
  • URL_GUIDE.md: documents the new friendly-path forms and removes the now-satisfied "NIP-05 username lookups in /u/ route" future-enhancement bullet.

Testing

  • npm run test — 1114 vitest tests pass (24 new in src/lib/profileLinks.test.ts, 5 new in src/pages/UniversalUserPage.test.tsx, 4 new in src/pages/SearchPage.test.tsx).
  • tsc -p tsconfig.app.json --noEmit — 0 errors.
  • eslint src/ — 0 errors (14 pre-existing warnings in unrelated files).
  • Manual verification on staging: visit /u/jacky and confirm the user's profile loads; visit /u/_%40jacky.divine.video and confirm it lands on the same profile.

Visuals

  • UI change with screenshots/video attached
  • No visual change
  • Visuals and text avoid sensitive external brand or partner names unless explicitly approved

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown

🚀 Preview Deployment

Property Value
Preview URL https://e13cd369.divine-web-fm8.pages.dev
Commit 3ff4fce
Branch fix/friendly-nip05-urls

@divinevideo divinevideo deleted a comment from CLAassistant Jun 10, 2026
jalcine added 2 commits June 10, 2026 17:46
- /u/_%40jacky.divine.video -> /u/jacky (bare local part on default apex)
- /u/_%40jacky.dvine.video -> /u/jacky.dvine.video (alternate apex)
- /u/alice%40primal.net -> /u/alice.primal.net (third-party NIP-05)
- /u/_%40jacky.divine.video and /u/jacky.divine.video and /u/jacky all
  resolve the same profile (kind-0 match against either canonical form,
  DNS NIP-05 fallback when the relay has no match)
- Drop the misleading openvine.co legacy fallback for subdomain-shaped
  inputs (was: 'Could not find a user with legacy Vine username or NIP-05
  identifier: jacky.divine.video' on /u/jacky.divine.video)
- Add URL_GUIDE.md entries for the new friendly-path forms

Refs: /u/_%40jacky.divine.video 404 in browser
The UserCard on /search?q=... only displayed the kind-0 name field,
falling back to a random genUserName(pubkey) when that was empty. For
users with a NIP-05 set, that meant the card showed e.g. @minimal Mouse 1
when the user's actual handle is _@jacky.divine.video — completely
disconnected from the network identity that buildProfileLinkPath now
routes to /u/jacky.

Prefer the NIP-05 in the displayed secondary line when present, using
the same format ProfileHeader uses (@jacky.divine.video for divine
NIP-05s, @<friendly-form> for third-party, @<name> as last resort).
Keeps the legacy @<kind-0 name> only as a fallback for users without
a NIP-05.
@jalcine jalcine force-pushed the fix/friendly-nip05-urls branch from 658d073 to 1a5405c Compare June 10, 2026 21:46
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 10, 2026

Copy link
Copy Markdown

Deploying divine-web with  Cloudflare Pages  Cloudflare Pages

Latest commit: 3ff4fce
Status: ✅  Deploy successful!
Preview URL: https://8c00a11a.divine-web.pages.dev
Branch Preview URL: https://fix-friendly-nip05-urls.divine-web.pages.dev

View logs

@jalcine jalcine self-assigned this Jun 10, 2026
jalcine added 2 commits June 10, 2026 22:51
…IP-05 segments

- Add normalizeUrlSegmentToFriendly to convert raw NIP-05 /u/ URL
  segments (e.g. _@jimmyhere.divine.video) to the canonical
  friendly form (jimmyhere).
- UniversalUserPage now renders ProfilePage directly via a new
  pubkeyOverride prop instead of navigating away to /<npub>,
  so the /u/<username> URL persists in the address bar.
- ProfilePage replaces the noisy full-page subdomain redirect
  (window.location.href) with a silent in-place replaceState
  swap to the /u/<username> vanity URL when a verified
  divine.video NIP-05 is detected on the apex domain.
…apex list

- Inline the normalizeUrlSegmentToFriendly logic into UniversalUserPage
  as a regex-based stripNip05Envelope helper. We deliberately do NOT
  resolve the raw _@<sub>.<apex> / <sub>@<apex> NIP-05 form — only the
  canonical /u/<sub> path runs through the lookup.
- Promote DIVINE_APEX_DOMAINS in nip05Utils.ts to a public export and
  feed the new regex from it (single source of truth for the apex list).
- Simplify the ProfilePage URL-rewrite pathname guard: the only
  reachable legacy entry point for ProfilePage is /profile/<npub>.
- Add two UniversalUserPage integration tests for percent-encoded and
  third-party segment passthrough.

@rabble rabble left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks — this is a thorough fix for #402 and the test coverage on the happy paths is great. Two functional issues need addressing before merge, plus a coordination question:

  1. Third-party NIP-05 links 404. buildProfileLinkPath emits /u/alice.primal.net, but nip05CandidatesFromUrlSegment returns only the literal dotted segment for non-apex domains — it never matches a kind-0 nip05 (those contain @), and the DNS fallback skips candidates without @. So every third-party link the app now generates lands on "User not found" (URL_GUIDE advertises this form as working). The comment in profileLinks.ts says callers derive local@domain separately, but UniversalUserPage doesn't. Suggest yielding alice@primal.net / _@alice.primal.net candidates for non-apex segments so the DNS fallback can try them.

  2. NIP05_ENVELOPE_PATTERN crashes on /u/dvine.video. The alternation ([^@]+)@divine\.video|dvine\.video isn't grouped, so the bare string dvine.video matches with groups 2 and 3 undefined, and (match[2] ?? match[3]).toLowerCase() throws inside the effect (verified in node). A profile with nip05: "_@dvine.video" makes the app generate exactly that link. Please wrap the apex list in (?:…), escape the dots in the first branch too, and guard against undefined groups.

  3. Search card shows unverified NIP-05. UserCard renders metadata.nip05 straight from kind-0 with no useNip05Validation, unlike ProfileHeader — anyone can claim _@jack.divine.video and show up as @jack.divine.video in search. Could we validate before display, or style it as clearly unverified?

  4. Heads-up: PR #404 overlaps. It removes nip05 from buildProfileLinkPath call sites in VideoCard/NoteContent (direct npub links), which is the opposite strategy to this PR. They don't conflict textually but merging both gives inconsistent profile URLs across surfaces — worth syncing on which approach is canonical before either lands.

Also flagging that the verified-NIP-05 subdomain redirect in ProfilePage becomes an apex-side /u/ rewrite — deliberate per the commit message, but since subdomain canonicalization is load-bearing in the router architecture, want to confirm that change has product sign-off. Smaller notes: prefer navigate(path, { replace: true }) over raw replaceState so React Router stays in sync, and confirm dropping the @openvine.co bare-name fallback is intentional.

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.

/u/_%40jacky.divine.video 404s; profile URLs use percent-encoded NIP-05s

2 participants