argus/DESIGN-root-tld-cert.md
Local Dev 2c307249a1 Collision modes (BCNR/ICANN) + root TLD cert + VPS electrum-source indexer
Theseus soft-mode UX: 'Open with...' modal on collision, per-name/per-TLD
overrides, live per-tab switcher in the site-info popover, and a Naming section
in Settings for policy + reset. Backed by an on-chain root TLD certificate
(tlds.bch) that resolver-web.js discovers via fetchBcnrTlds()/isBcnrNativeTld().

Companion pieces:
- Argus/src/indexer/ELECTRUM-SOURCE-README.md — the featherweight VPS variant
  (no BCHN node, no Fulcrum) now live as bns-indexer.service.
- Argus/DESIGN-root-tld-cert.md — clarified: NOT a governance workflow, just
  ordinary key management (single wallet MVP -> 2-of-3 multisig). List gates
  registration / surgical NRPT / soft-mode classifier — never resolution.
- ROADMAP-IDEAS.md — recorded SiaGit/GitHub.sia + user-friendly Sia UI ideas.

Full spec: Argus/DESIGN-collision-modes.md (already tracked).
2026-08-02 11:39:00 +02:00

5.5 KiB

Root TLD certificate (BCNR's IANA-root)

Status: ✅ deployed on chipnet (2026-07-30).

The authoritative list of BCNR-native TLDs — the "IANA root" for BCNR — lives in a single on-chain certificate. Clients read it to know which TLDs BCNR considers its own, which is what makes multi-TLD support and collision detection work without hardcoded lists.

The name

tlds.bch — registered on chipnet (see the wallet's index). One record:

  • tlds (string) — space-separated list of TLD suffixes, lowercase, [a-z0-9-]{1,16} each. Bootstrap set (2026-07-30): "bch p2p deviant nav sia dgb nt sm lm".

Chosen shape:

  • Space-separated string, not JSON. Fits the 200-byte OP_RETURN payload comfortably, mirrors the existing el electrum-server-list record (electrum.silentmode.bch), avoids escaping in on-chain payloads.
  • One well-known name at the well-known TLD (.bch). No chicken-and-egg — a client that only knows .bch can still resolve tlds.bch and learn the rest. This is the sole hardcoded name; the rest are on-chain.

Client API (resolver-web.js)

import { ROOT_TLD_NAME, BOOTSTRAP_TLDS,
         fetchBcnrTlds, getBcnrTlds, parseTldsRecord,
         isBcnrNativeTld } from "./resolver-web.js";

// One-shot: connect, read the on-chain list, return it
const tlds = await fetchBcnrTlds({ WebSocket, electrum: [...] });

// Or reuse an already-built index (no extra round-trip)
const tlds = getBcnrTlds(index);

// Classify a TLD
isBcnrNativeTld(tlds, "p2p")  // -> true
isBcnrNativeTld(tlds, "de")   // -> false

Fallbacks: unpublished / unreachable / empty → BOOTSTRAP_TLDS = ["bch"]. Callers should treat the list as live but cacheable (short TTL, e.g. one hour) — every consumer already has a name-index cache, so add the TLD read to the same warm path.

How to update the list

Adding a TLD is one on-chain UPDATE (proves ownership by moving the certificate):

import { loadWallet } from "./src/lib/wallet.js";
import { updateName } from "./src/lib/bns.js";
const w = await loadWallet();
await updateName(w, "tlds.bch", {
  tlds: "bch p2p deviant nav sia dgb nt sm lm bit test x silentmode",
});

Once the tx confirms and resolver indexes refresh (typically < 60 s), every client picks up the new TLD without a software update or reinstall.

Key management, not governance. The TLD list is a rarely-changed activation list (the "constitution"), not a workflow with continuous decisions. After launch, a new TLD gets added occasionally — quarterly, maybe. So the cert needs ordinary key hygiene, not a standing governance body:

  • MVP / chipnet: single operator wallet is fine.
  • Real deployment: 2-of-3 multisig (operator + backup keys) for bus-factor and key-loss protection. Not "decentralized governance" — just multiple keyholders to protect one occasionally-updated list.

Note the strict scope: the TLD cert has no editorial power — it doesn't decide what names may exist within a TLD (that's permissionless), doesn't take names away from existing owners, and doesn't moderate content. Moderation is a different concern, about sites not TLDs, continuous rather than occasional — that's where Athenae Politeia + paid members earn their keep (see ROADMAP-IDEAS.md §4). Keep the two systems separate.

What this unlocks — and what it does NOT

The root cert does NOT gate resolution. The resolver stays TLD-agnostic — any registered name under any TLD resolves. The activated list has three real consumers:

  1. Registrar (register.html) — the TLD dropdown for buyers is limited to activated TLDs. Storefront quality bar; keeps stray experimental TLDs out of the buying UI. Names already registered under unlisted TLDs still resolve.
  2. Surgical-mode routing (install.ps1 -Mode nrpt) — the installer reads the root cert and writes one NRPT rule per activated TLD. Adding a TLD on-chain + re-running the installer picks it up (no code change). Users who want any-TLD system-wide routing pick Full mode instead.
  3. Collision-mode classifier (soft "Open with…" prompt) — clients use it as a hint: "is this TLD BCNR-native → probably not a collision, don't prompt." A TLD not on the list falls through to normal collision handling; the list is a preemption aid, not a filter.

The resolver never consults the list. Registration and resolution are separate concerns; permissionless resolution + curated activation is deliberate.

Not in scope here

  • Per-TLD registry designation (which chain / operator backs each TLD, à la the .bit on BCH vs Namecoin question). The current record is flat; if we need per-TLD metadata, upgrade the record to tlds = space-separated tokens of shape <tld>|<registry>|…, mirroring el's url|ip shape. Or move to a Sia-hosted manifest referenced by hash in the record (unlocks arbitrary metadata without OP_RETURN size worries).
  • Root cert key ownership. For real use the key holding tlds.bch must be more than one operator's hot wallet — see the governance discussion in ROADMAP-IDEAS.md §4.

Test / verify

Any BNS resolver (local Fulcrum, VPS BNS indexer, public electrum) reads it:

import WebSocket from "ws";
import { fetchBcnrTlds } from "./src/lib/resolver-web.js";
console.log(await fetchBcnrTlds({
  WebSocket, electrum: [{ url: "ws://127.0.0.1:50003" }],
}));
// -> ["bch","p2p","deviant","nav","sia","dgb","nt","sm","lm"]

Registration tx (2026-07-30): f5a34ec7b995541f2f9b10f85d30b29cc76ebf75960e794a9001a68b16f6b3e9.