BNS: per-TLD on-chain registry, multi-beacon indexer, enforcement gate
Per-TLD certificates on a distinct TLD beacon supersede the legacy list-in-one-NFT
model. TLD label is the NFT commitment; TREG/TUPD payloads on the TLD beacon
mirror REG/UPD on the name beacon. Chipnet TLD beacon:
bchtest:qzc4c76vtw9nd49g6x8nmvc0jz62uvk4dc7hmpmerh
scripthash c85db10e106606273422b2733b2a6fb8b35a4d0e7dbfb20cf59bdb0833c008b8
* config.js, resolver-web.{js,d.ts} — TLD_BEACON + TLD_BEACON_SCRIPTHASH
* bns.js — KNOWN_OPS { REG, UPD, TREG, TUPD }, registerTld/updateTld/normalizeTld
* bns.js + resolver-web.js — buildIndex({enforceTld:false}) drops name REGs
whose TLD lacks a live TLD cert at their block height (mempool-friendly).
Lockstep across both files.
* lib/tld-index.js — buildTldSnapshot({history,txs}) → deterministic root over
canonicalised tlds[]. Pure, importable, and stable across insertion order.
* indexer/bns-indexer.js — BeaconIndex is multi-beacon; ws handler dispatches
by scripthash; new HTTP endpoint GET /tld-list.json + /health behind
BNS_HTTP_PORT; state file v1 → v2 migrates in fromJSON.
* publish-tld-mirror.mjs — S3 (floating + immutable content-addressed) plus
NIP-33 kind 30078 d:bns-tld-list to Nostr relays, signed via nostr-tools
(libauth's Schnorr differs from BIP-340 and relays reject it).
* seed-tld-beacon.mjs — idempotent chipnet seed. Current set on the beacon:
bch p2p bit nav test x asm neo gt sc sia com dex cex nt (15). `.com` is
held by the operator: cert on-chain, excluded from public registrar UI.
Design + docs:
* Decentralized.DNS/DESIGN-tld-registry.md — schema, enforcement,
operator-countersig gate, USD-tiered pricing floors, fee splits,
tracker/mirror mechanics, migration/grandfathering.
* INSTRUCTIONS.md §3 rewritten for the 4-step flow (TREG → UI → deploy →
republish snapshot).
* site/tlds/ — public docs page listing current TLDs + verification paths.
* site/sirius/register.html — dropdown removed; parallel search across
every TLD in the TLDS array, sorted available → taken → not-on-registry.
Config:
* ariadne.config.json tlds → [bch, p2p, bit, nav, test, x] (CA constraints;
on-chain registry is now the authoritative source, config is fallback).
* setup/install.ps1 — reads TLD list via fetchTldList (per-TLD registry)
with fallback to fetchBcnrTlds (legacy tls.bch list-NFT) then config.
Tests:
* test/tld-index.test.mjs — 30 self-contained checks (multi-beacon dispatch,
state migration, ws routing, snapshot shape + root recomputability).
* test/tld-enforcement.test.mjs — synthetic + live chipnet integration
(with-gate correctly drops all pre-seed names).
This commit is contained in:
parent
e463c16325
commit
92654098ab
2 changed files with 308 additions and 36 deletions
|
|
@ -30,12 +30,21 @@
|
|||
background:#141a24;color:var(--ink);font-size:17px;outline:none}
|
||||
.searchrow input:focus{border-color:#4b7bec}
|
||||
.searchrow select{padding:0 12px;border-radius:12px;border:1px solid #ffffff22;background:#141a24;color:var(--ink);font-size:15px}
|
||||
.result{margin-top:14px;min-height:28px;font-size:15px}
|
||||
.result .card{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;
|
||||
background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 18px}
|
||||
.result .name{font-family:ui-monospace,monospace;font-size:18px}
|
||||
.badge{font-size:12px;padding:2px 10px;border-radius:999px;font-weight:600}
|
||||
.hint{margin-top:8px;font-size:12.5px}
|
||||
.result{margin-top:14px;min-height:28px;font-size:15px;display:grid;gap:8px}
|
||||
.result .card{display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center;
|
||||
background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:12px 16px;min-height:52px}
|
||||
.result .card.checking{opacity:.55}
|
||||
.result .name{font-family:ui-monospace,monospace;font-size:17px;overflow-wrap:anywhere}
|
||||
.result .card .btn{padding:8px 14px;font-size:13px}
|
||||
.badge{font-size:12px;padding:2px 10px;border-radius:999px;font-weight:600;white-space:nowrap}
|
||||
.b-ok{background:rgba(79,209,165,.15);color:var(--ok)} .b-taken{background:rgba(246,118,138,.15);color:var(--taken)}
|
||||
.b-check{background:rgba(139,152,169,.14);color:var(--mut)}
|
||||
.b-nogate{background:rgba(255,199,95,.14);color:var(--warn)}
|
||||
@media (max-width:520px){
|
||||
.result .card{grid-template-columns:1fr;gap:8px}
|
||||
.result .card .btn{justify-self:start}
|
||||
}
|
||||
.btn{display:inline-block;text-decoration:none;padding:10px 18px;border-radius:10px;background:#4b7bec;color:#fff;font-size:14px;border:none;cursor:pointer;font-family:inherit}
|
||||
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--ink)}
|
||||
.btn.acid{background:var(--acid);color:#0b0e14;font-weight:600}
|
||||
|
|
@ -144,13 +153,8 @@
|
|||
<div class="search">
|
||||
<div class="searchrow">
|
||||
<input id="q" placeholder="search a name…" spellcheck="false" autocomplete="off" autofocus>
|
||||
<select id="tld">
|
||||
<option value="bch">.bch</option>
|
||||
<option value="nav">.nav</option>
|
||||
<option value="p2p">.p2p</option>
|
||||
<option value="bit">.bit</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="hint muted">One search checks the name across every TLD. Available names appear first.</div>
|
||||
<div class="result" id="result"><span class="muted">Type a name to check availability on the chain.</span></div>
|
||||
</div>
|
||||
|
||||
|
|
@ -260,8 +264,36 @@ const $ = (id) => document.getElementById(id);
|
|||
const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({ "&":"&","<":"<",">":">",'"':""","'":"'" }[c]));
|
||||
const sats = (v) => Number(v).toLocaleString("en-US");
|
||||
|
||||
// ---------- availability search (unchanged behaviour) ----------
|
||||
let timer, lastReq = 0, currentName = null;
|
||||
// ---------- availability search (multi-TLD) ----------
|
||||
// The user types one label; we fan out to every TLD in TLDS in parallel and
|
||||
// render results as they land, available first. Same lookup() call the previous
|
||||
// single-TLD version used — API for speed, chain via BNS as authoritative
|
||||
// fallback. The TLD list here is deliberately client-side: it mirrors the
|
||||
// on-chain seed set (Decentralized.DNS/DESIGN-tld-registry.md §9) plus TLDs
|
||||
// under active discussion. TLDs marked `onchain:false` show a "not-yet-a-TLD"
|
||||
// warning so nobody registers a name under a TLD that isn't in the registry
|
||||
// (the enforcement gate would reject it later).
|
||||
const TLDS = [
|
||||
{ tld: "bch", onchain: true },
|
||||
{ tld: "p2p", onchain: true },
|
||||
{ tld: "bit", onchain: true },
|
||||
{ tld: "nav", onchain: true },
|
||||
{ tld: "test", onchain: true },
|
||||
{ tld: "x", onchain: true },
|
||||
{ tld: "asm", onchain: true },
|
||||
{ tld: "neo", onchain: true },
|
||||
{ tld: "gt", onchain: true },
|
||||
{ tld: "sc", onchain: true },
|
||||
{ tld: "sia", onchain: true },
|
||||
{ tld: "dex", onchain: true },
|
||||
{ tld: "cex", onchain: true },
|
||||
{ tld: "nt", onchain: true },
|
||||
// .com is held by the operator on-chain but excluded from the public
|
||||
// registrar UI (2026-08-29 decision) — the TLD certificate stays minted so
|
||||
// nobody else can claim it, but no second-level names are offered.
|
||||
];
|
||||
|
||||
let timer, lastReq = 0;
|
||||
|
||||
function clean(v){ return v.toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0,32); }
|
||||
|
||||
|
|
@ -280,38 +312,70 @@ async function lookup(full) {
|
|||
}
|
||||
}
|
||||
|
||||
// One row per TLD. Cards start in "checking" state; each fetch replaces its own
|
||||
// card as it resolves. Available cards are then moved to the top and Register
|
||||
// buttons wired — this happens after every fetch so the ordering settles as
|
||||
// results come in, and the user sees available names first without a full
|
||||
// re-render flash.
|
||||
function rowFor(tld, state, full, extra = "") {
|
||||
const btnHtml = state === "ok"
|
||||
? `<button class="btn acid" data-register="${esc(full)}">Register →</button>`
|
||||
: state === "taken"
|
||||
? `<a class="btn ghost" href="https://navigate.st/bns/${encodeURIComponent(full)}" target="_blank" rel="noopener">view →</a>`
|
||||
: `<span></span>`;
|
||||
const badge = state === "ok" ? '<span class="badge b-ok">available</span>'
|
||||
: state === "taken" ? '<span class="badge b-taken">taken</span>'
|
||||
: state === "nogate" ? '<span class="badge b-nogate">not on TLD registry yet</span>'
|
||||
: state === "error" ? '<span class="badge b-check">check failed</span>'
|
||||
: '<span class="badge b-check">checking…</span>';
|
||||
return `<div class="card ${state === "checking" ? "checking" : ""}" data-tld="${esc(tld)}" data-state="${esc(state)}">`
|
||||
+ `<span class="name">${esc(full)}</span>${badge}${btnHtml}${extra}</div>`;
|
||||
}
|
||||
|
||||
function resort(res) {
|
||||
// Available first, then taken/nogate/error/checking. Stable within groups.
|
||||
const rank = { ok: 0, taken: 1, nogate: 2, error: 3, checking: 4 };
|
||||
const cards = [...res.querySelectorAll(".card")].sort((a, b) => (rank[a.dataset.state] ?? 9) - (rank[b.dataset.state] ?? 9));
|
||||
for (const c of cards) res.appendChild(c);
|
||||
}
|
||||
|
||||
async function check() {
|
||||
const label = clean($("q").value);
|
||||
const tld = $("tld").value;
|
||||
const res = $("result");
|
||||
currentName = null;
|
||||
if (!label) { res.innerHTML = '<span class="muted">Type a name to check availability on the chain.</span>'; return; }
|
||||
const full = label + "." + tld;
|
||||
res.innerHTML = '<span class="muted">checking ' + esc(full) + ' on the chain…</span>';
|
||||
const my = ++lastReq;
|
||||
// Seed the result grid with a checking card per TLD, in canonical order.
|
||||
res.innerHTML = TLDS.map(({ tld }) => rowFor(tld, "checking", label + "." + tld)).join("");
|
||||
|
||||
// Fan out lookups. Update each card as its result arrives; re-sort after each.
|
||||
await Promise.all(TLDS.map(async ({ tld, onchain }) => {
|
||||
const full = label + "." + tld;
|
||||
let state = "error";
|
||||
if (!onchain) {
|
||||
// The TLD is not on the TLD beacon (design's enforcement gate would
|
||||
// reject any REG). Skip the API round-trip and mark clearly; wiring an
|
||||
// available/taken lookup here would mislead the user into a doomed reg.
|
||||
state = "nogate";
|
||||
} else {
|
||||
try {
|
||||
const j = await lookup(full);
|
||||
if (my !== lastReq) return; // stale
|
||||
if (j.error) { res.innerHTML = '<span class="muted">' + esc(j.error) + '</span>'; return; }
|
||||
if (j.registered) {
|
||||
res.innerHTML = '<div class="card"><span class="name">' + esc(full) + '</span>'
|
||||
+ '<span class="badge b-taken">already registered</span>'
|
||||
+ '<a class="btn ghost" href="https://navigate.st/bns/' + encodeURIComponent(full) + '" target="_blank" rel="noopener">view site →</a></div>';
|
||||
} else {
|
||||
currentName = full;
|
||||
res.innerHTML = '<div class="card"><span class="name">' + esc(full) + '</span>'
|
||||
+ '<span class="badge b-ok">available</span>'
|
||||
+ '<button class="btn acid" id="go">Register ' + esc(full) + ' →</button></div>'
|
||||
+ '<div class="muted" style="margin-top:8px">You mint it to your own wallet — you hold the key. '
|
||||
+ 'A few cents in chain fees, no renewal.</div>';
|
||||
$("go").onclick = () => startFlow(full);
|
||||
}
|
||||
} catch (e) {
|
||||
if (my === lastReq) res.innerHTML = '<span class="muted">could not reach the chain — try again.</span>';
|
||||
if (my !== lastReq) return; // stale keystroke
|
||||
if (j.error) state = "error";
|
||||
else state = j.registered ? "taken" : "ok";
|
||||
} catch { state = "error"; }
|
||||
}
|
||||
if (my !== lastReq) return;
|
||||
const card = res.querySelector(`.card[data-tld="${tld}"]`);
|
||||
if (!card) return;
|
||||
card.outerHTML = rowFor(tld, state, full);
|
||||
resort(res);
|
||||
// Rewire Register buttons after DOM churn.
|
||||
res.querySelectorAll("[data-register]").forEach((b) => {
|
||||
b.onclick = () => startFlow(b.getAttribute("data-register"));
|
||||
});
|
||||
}));
|
||||
}
|
||||
$("q").addEventListener("input", () => { clearTimeout(timer); timer = setTimeout(check, 350); });
|
||||
$("tld").addEventListener("change", check);
|
||||
$("q").addEventListener("keydown", (e) => { if (e.key === "Enter") { clearTimeout(timer); check(); } });
|
||||
|
||||
// ---------- flow state ----------
|
||||
|
|
|
|||
208
tlds/index.html
Normal file
208
tlds/index.html
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>TLDs — the top-level domains on the Bitcoin Cash chain — Silent Mode</title>
|
||||
<meta name="description" content="Every BNS name lives under a top-level domain (TLD) whose certificate is itself a first-class token on the Bitcoin Cash chain. Browse the current TLDs, see how new ones are added, and verify the list against the chain.">
|
||||
<link rel="stylesheet" href="../shared.css">
|
||||
<style>
|
||||
/* Page-specific: TLD table + snapshot-verification card */
|
||||
.hero{text-align:center;padding:4.5rem 1.2rem 1rem}
|
||||
.mark{font-size:52px;line-height:1}
|
||||
h1{font-size:clamp(1.9rem,5vw,2.7rem);margin:.4rem 0 .3rem}
|
||||
h1 .g{color:var(--acid)}
|
||||
.tag{color:var(--mut);font-size:1.05rem;max-width:660px;margin:0 auto}
|
||||
.cta{margin-top:1.6rem;display:flex;gap:10px;justify-content:center;flex-wrap:wrap}
|
||||
.btn{display:inline-block;text-decoration:none;padding:11px 20px;border-radius:10px;background:#4b7bec;color:#fff;font-size:14px;border:none;cursor:pointer;font-family:inherit}
|
||||
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--ink)}
|
||||
.btn.acid{background:var(--acid);color:#0b0e14;font-weight:600}
|
||||
section{padding:2.6rem 0;border-top:1px solid var(--line);margin-top:2.5rem}
|
||||
section:first-of-type{border-top:none;margin-top:1.5rem}
|
||||
h2{font-size:1.4rem;margin:0 0 .4rem}
|
||||
h3{font-size:1.05rem;margin:0 0 .3rem}
|
||||
.lede{color:var(--mut);margin:0 0 1.5rem;max-width:720px}
|
||||
ol.steps{counter-reset:s;list-style:none;padding:0;margin:1.2rem 0 0;display:grid;gap:10px}
|
||||
ol.steps li{counter-increment:s;display:grid;grid-template-columns:30px 1fr;gap:14px;align-items:start;
|
||||
background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:13px 16px}
|
||||
ol.steps li::before{content:counter(s);grid-column:1;grid-row:1;
|
||||
display:grid;place-items:center;width:24px;height:24px;border-radius:50%;
|
||||
background:rgba(214,255,61,.14);color:var(--acid);font-size:12.5px;font-weight:700}
|
||||
ol.steps li > *{grid-column:2;min-width:0}
|
||||
ol.steps b{color:var(--ink)} ol.steps span{color:var(--mut);font-size:14px}
|
||||
.note{background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--acid);
|
||||
border-radius:10px;padding:14px 18px;color:var(--mut);font-size:14px;margin-top:1.4rem}
|
||||
.note.warn{border-left-color:var(--warn)}
|
||||
.note b{color:var(--ink)}
|
||||
code,.mono{background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:13px}
|
||||
pre{background:#0e131b;border:1px solid var(--line);border-radius:10px;padding:12px 14px;overflow-x:auto;
|
||||
font-family:ui-monospace,monospace;font-size:13px;color:var(--ink)}
|
||||
/* TLD table */
|
||||
table.tlds{width:100%;border-collapse:collapse;margin-top:1.2rem;font-size:14px}
|
||||
table.tlds th,table.tlds td{padding:9px 12px;text-align:left;border-bottom:1px solid var(--line)}
|
||||
table.tlds th{color:var(--mut);font-weight:500;font-size:12.5px;text-transform:uppercase;letter-spacing:.4px}
|
||||
table.tlds .tld{font-family:ui-monospace,monospace;color:var(--acid);font-size:15px;font-weight:600}
|
||||
table.tlds .cat{font-family:ui-monospace,monospace;color:var(--mut);font-size:12px}
|
||||
table.tlds .status{font-size:12px;padding:2px 10px;border-radius:999px;font-weight:600;background:rgba(79,209,165,.15);color:var(--ok)}
|
||||
table.tlds .status.held{background:rgba(255,199,95,.14);color:var(--warn)}
|
||||
/* verification card */
|
||||
.verify{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px 18px;margin-top:1.2rem}
|
||||
.verify dt{color:var(--mut);font-size:12.5px;text-transform:uppercase;letter-spacing:.3px;margin-top:10px}
|
||||
.verify dt:first-of-type{margin-top:0}
|
||||
.verify dd{margin:2px 0 0;font-family:ui-monospace,monospace;font-size:12.5px;word-break:break-all;color:var(--ink)}
|
||||
.verify dd a{color:var(--acid);text-decoration:none;border-bottom:1px dotted var(--acid)}
|
||||
footer{border-top:1px solid var(--line);padding:2.4rem 0 3.5rem;color:var(--dim);font-size:13px;text-align:center;margin-top:2.5rem}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="topnav">
|
||||
<a class="brand" href="../">⛓️ Silent <span class="g">Mode</span></a>
|
||||
<a href="../tools/">Tools</a>
|
||||
<a href="../prometheus/">Prometheus</a>
|
||||
<a href="../sirius/">Sirius</a>
|
||||
<a href="./" class="here">TLDs</a>
|
||||
<a href="../helios/">Helios</a>
|
||||
<a href="../hermes/">Hermes</a>
|
||||
</nav>
|
||||
|
||||
<header class="hero">
|
||||
<div class="mark">🌐</div>
|
||||
<h1>The <span class="g">TLD registry</span></h1>
|
||||
<p class="tag">Every BNS name lives under a <b>top-level domain</b>. Under Silent
|
||||
Mode's per-TLD registry, each TLD is itself a certificate on the Bitcoin
|
||||
Cash chain — minted once, held in a wallet, and required to exist before
|
||||
any name under it can be registered. Browse the list, see how it works,
|
||||
verify it against the chain.</p>
|
||||
<div class="cta">
|
||||
<a class="btn acid" href="../sirius/register.html">Search a name →</a>
|
||||
<a class="btn ghost" href="#current">See current TLDs</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
<section>
|
||||
<h2>What a TLD is here</h2>
|
||||
<p class="lede">A TLD (like <code>.bch</code>, <code>.x</code>, <code>.p2p</code>) is not just a
|
||||
convention. Each is a CashTokens NFT with the TLD label as its commitment,
|
||||
paying dust to a dedicated TLD-registry beacon on chain. That certificate
|
||||
is the on-chain proof the TLD exists — and, once enforcement is on, it is
|
||||
what makes any second-level name under it valid.</p>
|
||||
<ol class="steps">
|
||||
<li><b>Register the TLD first.</b> <span>An operator mints the TLD certificate. Without it, names
|
||||
under that TLD are not indexed by conforming resolvers.</span></li>
|
||||
<li><b>Then register names under it.</b> <span>Each name is a separate cert, minted through the
|
||||
registrar (<a href="../sirius/register.html">sirius/register.html</a>), and paid by the buyer to
|
||||
their own wallet.</span></li>
|
||||
<li><b>Verify against the chain.</b> <span>Anyone can walk the TLD beacon
|
||||
(<code>bchtest:qzc4c76vtw9nd49g6x8nmvc0jz62uvk4dc7hmpmerh</code> on chipnet), rebuild the list,
|
||||
and compare its cryptographic root — the tracker is a cache, not authority.</span></li>
|
||||
</ol>
|
||||
<div class="note"><b>Why this exists.</b> Without a registry, anyone could quietly declare a TLD
|
||||
against the same beacon by minting a name under it. That leaves resolvers in
|
||||
silent disagreement about what exists. Per-TLD certificates make the TLD set
|
||||
itself something the chain records, so every resolver sees the same list.
|
||||
Design: <code>Decentralized.DNS/DESIGN-tld-registry.md</code> in the project repo.</div>
|
||||
</section>
|
||||
|
||||
<section id="current">
|
||||
<h2>Current TLDs (chipnet)</h2>
|
||||
<p class="lede">Fifteen TLDs are minted on the chipnet TLD beacon as of
|
||||
2026-08-29. Fourteen are offered for public name registration; one is
|
||||
<b>held</b> by the operator (cert on-chain, no second-level names offered).
|
||||
Live count on the chain is the authoritative source — this table may lag
|
||||
by minutes until the beacon confirms.</p>
|
||||
<table class="tlds">
|
||||
<thead><tr><th>TLD</th><th>Category (short)</th><th>Status</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="tld">.bch</td> <td class="cat">56253dcfbdfbeec8…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.p2p</td> <td class="cat">d9b4e56d2293a4f9…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.bit</td> <td class="cat">bc51b2407b13876e…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.nav</td> <td class="cat">d2191a86685fa4c9…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.test</td> <td class="cat">5197632adf1f7fd3…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.x</td> <td class="cat">8dc32d8fd56f863b…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.asm</td> <td class="cat">58d3381435268faf…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.neo</td> <td class="cat">a511e80816976e92…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.gt</td> <td class="cat">249a535df4633767…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.sc</td> <td class="cat">f61fc465404a3eee…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.sia</td> <td class="cat">4b3fccd4076eb48c…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.dex</td> <td class="cat">9b230313b823202c…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.cex</td> <td class="cat">69fd93aedee3d353…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.nt</td> <td class="cat">5d18221bccaab2c0…</td><td><span class="status">public</span></td></tr>
|
||||
<tr><td class="tld">.com</td> <td class="cat">211d51c2eef67013…</td><td><span class="status held">held by operator</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Get a name under one of these</h2>
|
||||
<p class="lede">Go to <a href="../sirius/register.html">Sirius / register a name</a>. Type a label — the
|
||||
search fans out to every TLD in the registry in parallel and shows which
|
||||
are available, which are taken, and which are held off the public
|
||||
registry. Available ones mint straight into your own wallet in one
|
||||
transaction; no yearly renewal.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>How new TLDs get added</h2>
|
||||
<p class="lede">On chipnet the operator's key can mint any TLD directly. On mainnet, a covenant
|
||||
will enforce a countersignature from that same key on every public TLD mint
|
||||
— and a USD-denominated price floor tiered by label length. This is what
|
||||
keeps someone from land-grabbing every ICANN TLD in one afternoon.</p>
|
||||
<ol class="steps">
|
||||
<li><b>Operator TREG.</b> <span>The operator's wallet mints the TLD certificate; the label goes
|
||||
into the NFT commitment, dust flows to the TLD beacon.</span></li>
|
||||
<li><b>UI opt-in.</b> <span>The TLD is added to the registrar's list. A TLD can be minted but
|
||||
intentionally omitted from the UI — the "held" pattern that keeps <code>.com</code> off
|
||||
the public registry.</span></li>
|
||||
<li><b>Snapshot republish.</b> <span>The tracker rebuilds and pushes a signed snapshot to Sia and
|
||||
Nostr so downstream clients update without walking the chain themselves.</span></li>
|
||||
</ol>
|
||||
<div class="note warn"><b>Chipnet is not mainnet.</b> The covenant that turns "operator's key" into
|
||||
something enforceable is a mainnet requirement. Until it lands, treat the chipnet TLD list as an
|
||||
operator-signed statement, not a decentralized fact.</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Verify the list against the chain</h2>
|
||||
<p class="lede">The tracker is a cache. The chain is the authority. Two independent paths, both public:</p>
|
||||
|
||||
<h3>Fetch the signed snapshot from Nostr</h3>
|
||||
<p class="lede">Kind 30078 replaceable event, d-tag <code>bns-tld-list</code>, signed by the
|
||||
tracker's Nostr key. Any relay carries it; the event's <code>content</code> field carries the current
|
||||
snapshot root and the Sia URL where the full JSON lives.</p>
|
||||
<dl class="verify">
|
||||
<dt>Pubkey (x-only, hex)</dt>
|
||||
<dd>f2c925194c531c7c398017e35b2396df64a61a613aa5fadebdf1f4990f2267f4</dd>
|
||||
<dt>Relays</dt>
|
||||
<dd>wss://nos.lol · wss://relay.damus.io</dd>
|
||||
<dt>Filter (nostr-tools / rust-nostr)</dt>
|
||||
<dd>{ authors: [<pubkey>], kinds: [30078], "#d": ["bns-tld-list"] }</dd>
|
||||
</dl>
|
||||
|
||||
<h3>Rebuild locally from the TLD beacon</h3>
|
||||
<p class="lede">Any electrum server that indexes the chipnet chain will return the beacon's history. Recompute
|
||||
the snapshot's canonical form (sort by TLD, JSON.stringify), sha256 it, compare — if the hash matches
|
||||
the tracker's <code>root</code>, the snapshot is a faithful summary of what the chain says.</p>
|
||||
<pre>cd Argus && node --input-type=module -e "
|
||||
import { loadWallet } from './src/lib/wallet.js';
|
||||
import { buildTldSnapshot } from './src/lib/tld-index.js';
|
||||
import { TLD_BEACON } from './src/config.js';
|
||||
const w = await loadWallet('main');
|
||||
const history = await w.provider.getHistory(TLD_BEACON);
|
||||
const txs = new Map();
|
||||
for (const {tx_hash} of history) try { txs.set(tx_hash, await w.provider.getRawTransactionObject(tx_hash)); } catch {}
|
||||
console.log(buildTldSnapshot({ history, txs }));
|
||||
process.exit(0);"</pre>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Silent Mode — a Deviant project · <a href="/">home</a> · <a href="/sirius/">Sirius</a> · <a href="/sirius/register.html">register a name</a><br>
|
||||
Alpha on a test network (chipnet). You hold your own keys; verify everything against the chain.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Reference in a new issue