aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-28 20:53:13 -0700
committerAdam Soltys <[email protected]>2026-08-28 20:53:13 -0700
commitef1ed09ec688ce27b8a2c3c957515f4dc7ab0596 (patch)
tree33a4ba8ee92f467cb5fcd9fa4796a862d07c80ff
parenta056ea615498ee62e4180c1e09b3ce0a5a03de0f (diff)
downloadcoinosv3-ef1ed09ec688ce27b8a2c3c957515f4dc7ab0596.tar.gz
coinosv3-ef1ed09ec688ce27b8a2c3c957515f4dc7ab0596.zip
A name's proof is its key, not a flag: takenByCoinosUser stops trusting `migrated` (which let poppy get squatted) — an existing coinos.io name yields only to a claim signed by that account's own nostr key; the squatted record, its DNS entry and its offer are evicted
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
-rw-r--r--names/server.js13
1 files changed, 8 insertions, 5 deletions
diff --git a/names/server.js b/names/server.js
index e49da51..e713ff4 100644
--- a/names/server.js
+++ b/names/server.js
@@ -572,15 +572,18 @@ const npubPrefixOwner = (name) => (/^npub1[a-z0-9]+$/.test(name) ? name : null);
// coinos.io names that belong to existing coinos users are reserved for
// them until the migration gives those users a way to claim their own.
-async function takenByCoinosUser(domain, name) {
+async function takenByCoinosUser(domain, name, claimantPubkey = null) {
if (domain !== 'coinos.io') return false;
try {
const r = await fetch(`https://coinos.io/api/users/${encodeURIComponent(name)}`);
if (r.status !== 200) return false;
- // A migrated account has handed its name over: the old site keeps the
- // account and its history, but receiving belongs to us now.
const u = await r.json().catch(() => null);
- return !(u && u.migrated);
+ if (!u) return false;
+ // An existing coinos.io account owns its name. The `migrated` flag once
+ // gated this and proved meaningless (poppy got squatted through it) —
+ // identity is the proof: only a claim SIGNED by the old account's own
+ // nostr key may take the name over.
+ return !(u.npub && claimantPubkey && u.npub === npubEncode(claimantPubkey));
} catch { return true; } // can't verify → refuse rather than squat
}
@@ -1044,7 +1047,7 @@ Bun.serve({
if (existing && existing.pubkey !== auth.pubkey && existing.manager !== auth.pubkey) {
return json({ error: 'name is taken' }, 409);
}
- if (!existing && await takenByCoinosUser(domain, name)) return json({ error: 'name is taken' }, 409);
+ if (!existing && await takenByCoinosUser(domain, name, auth.pubkey)) return json({ error: 'name is taken' }, 409);
// an npub-shaped name belongs to that identity alone
if (npubPrefixOwner(name)) {
const managerOk = existing && existing.manager === auth.pubkey;