diff options
| author | Adam Soltys <[email protected]> | 2026-08-27 23:05:21 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-27 23:05:21 -0700 |
| commit | ff9b87bb48130f638249412848c69b2d4a2a770a (patch) | |
| tree | ffcf243f167883d62573c0f0120661de0bc40451 | |
| parent | 8008239b74ad7849f100fbd5324883525f5a514b (diff) | |
| download | coinosv3-ff9b87bb48130f638249412848c69b2d4a2a770a.tar.gz coinosv3-ff9b87bb48130f638249412848c69b2d4a2a770a.zip | |
The chosen name outranks the placeholder everywhere: a claim can no longer release a custom name to make room for an npub default (the race that deleted passkeyguy), sign-in asks the registrar about the login identity before reaching for a placeholder, and the registrar answers /pubkey with the name the user picked rather than whichever record is younger
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
| -rw-r--r-- | names/server.js | 8 | ||||
| -rw-r--r-- | src/features/names.js | 24 |
2 files changed, 30 insertions, 2 deletions
diff --git a/names/server.js b/names/server.js index f54864d..e49da51 100644 --- a/names/server.js +++ b/names/server.js @@ -764,7 +764,13 @@ Bun.serve({ const domFilter = (url.searchParams.get('domain') || '').toLowerCase(); const mine = Object.entries(state.names) .filter(([k, r]) => r.pubkey === pm[1] && (!domFilter || k.endsWith('@' + domFilter))) - .sort((a, b) => (b[1].updated || 0) - (a[1].updated || 0)); + // a custom name outranks the free npub placeholder regardless of + // which record is fresher — a placeholder re-claimed on a new device + // must not shadow the name the user actually chose + .sort((a, b) => { + const ph = ([k]) => (/^npub1/.test(k.split('@')[0]) ? 1 : 0); + return ph(a) - ph(b) || (b[1].updated || 0) - (a[1].updated || 0); + }); if (!mine.length) return json({}); const [key, r] = mine[0]; const [name, domain] = key.split('@'); diff --git a/src/features/names.js b/src/features/names.js index 56fc77d..7096955 100644 --- a/src/features/names.js +++ b/src/features/names.js @@ -102,7 +102,13 @@ export function namesFeature(ctx) { }, signer); const prev = load().name; save({ name, domain: DOMAIN(), uri, offerPk: hook('nwcOfferPubkey') || null, updated: Date.now() }); - if (prev && prev !== name) { + // Releasing the previous name is for renames the user meant. A CUSTOM + // name must never be released because a placeholder claim raced in: that + // exact race once deleted a user's claimed name — adoptIdentity's npub + // claim landed in the same instant refresh() was adopting their real + // name, and `prev` here picked up the fresher read. + const demotion = /^npub1/.test(name) && prev && !/^npub1/.test(prev); + if (prev && prev !== name && !demotion) { post('/register', 'DELETE', { name: prev, domain: DOMAIN() }).catch(() => {}); // the old address just died — a kind 0 still pointing at it would send // zaps into the void, so the profile's lud16/nip05 follow the rename @@ -223,6 +229,22 @@ export function namesFeature(ctx) { // A restored wallet may not know its name yet — ask the registrar before // assuming there is none. if (!st.name) { await lookupMine(); st = load(); } + // The LOGIN identity may already own a custom name, claimed in a past + // session or on another device — find and adopt it BEFORE reaching for a + // placeholder (lookupMine only asks by the wallet's own key). + if (!st.name || /^npub1/.test(st.name)) { + try { + const r = await withTimeout( + fetch(`${REGISTRAR}/pubkey/${signer.pubkey}?domain=${encodeURIComponent(DOMAIN())}`).then((x) => x.json()), + 8000, 'registrar'); + if (r && r.name && !/^npub1/.test(r.name)) { + save({ ...load(), name: r.name, domain: r.domain || DOMAIN(), uri: r.uri }); + render(); + return r.name; + } + } catch {} + st = load(); // refresh() may have adopted a name while we were looking + } // The npub default is a placeholder for wallets with no name. Adopting it // must never displace a custom name — claim() releases the previous name, // so getting this wrong deletes the user's address from the registrar. |
