aboutsummaryrefslogtreecommitdiffstats
path: root/lib/names.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/names.ts')
-rw-r--r--lib/names.ts37
1 files changed, 37 insertions, 0 deletions
diff --git a/lib/names.ts b/lib/names.ts
new file mode 100644
index 00000000..284c595d
--- /dev/null
+++ b/lib/names.ts
@@ -0,0 +1,37 @@
+import { warn } from "$lib/logging";
+import { fail } from "$lib/utils";
+
+// The v3 registrar (coinos v3 / halwallet) owns any name it has a record for:
+// routes/lnurl.ts serves a claimed name's lightning address from the registrar
+// ahead of the local account. So a local account taking a name the registrar
+// holds silently hands its incoming payments to whoever holds the v3 name.
+// Both places that assign a username — registration and the profile rename —
+// must therefore refuse a name the registrar holds, unless the account's own
+// nostr key is the holder (a user re-creating their migrated name is fine).
+const NAMES_URL = process.env.NAMES_URL || "https://names.coinos.io";
+const DOMAIN = "coinos.io";
+
+// Fail CLOSED: if the registrar can't be reached we don't know who owns the
+// name, and handing out a squatted name is worse than a retry.
+export const assertNameFree = async (username: string, ownPubkey?: string) => {
+ const name = username.toLowerCase();
+ let body: any;
+ try {
+ const r = await fetch(
+ `${NAMES_URL}/name/${encodeURIComponent(name)}?domain=${DOMAIN}`,
+ { signal: AbortSignal.timeout(3000) },
+ );
+ if (!r.ok) throw new Error(`registrar said ${r.status}`);
+ body = await r.json();
+ } catch (e: any) {
+ warn("names registrar unreachable checking", name, e?.message);
+ fail("Username unavailable right now, please try again");
+ }
+ // `pubkey` is only present for a real v3 record. `taken` without one means
+ // the registrar is echoing our own account list back at us — not a claim.
+ const holder = body?.taken && typeof body.pubkey === "string" ? body.pubkey : null;
+ if (holder && holder !== ownPubkey) {
+ warn("username held by v3 registrar", name, holder.slice(0, 8));
+ fail(`Username ${name} taken`);
+ }
+};