From 0f144d94b9bf136c5b1f3e8c2e0da9abc7035490 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Fri, 31 Jul 2026 23:09:23 +0200 Subject: [PATCH] Resolver 3438d558: ASCII-clean install.ps1 + multi-TLD NRPT + VPS-first electrum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parallel session was right β€” the previously-shipped f94834b5 pre-dated: 1. the ASCII em-dash sweep in setup/install.ps1 (Inno's [Run] uses powershell.exe / PS 5.1; em-dashes in the file crashed the parser under UAC, so the installer copied files but never registered the scheduled task or NRPT rule) 2. the multi-TLD NRPT expansion (added .p2p .bit .nav alongside .bch) 3. the VPS-primary electrum server list β€” resolver-web.js now dials wss://coinspectrum.duckdns.org:50011 first (Silent Mode's own BCHN-backed BNS-only indexer), public servers as fallback Result: a real user installing 3438d558 on a fresh Windows box now gets a working system-wide resolver with no manual steps. Deployed to dl.silentmode.st and mirrored to Sia bns/silentmode/. --- DESIGN-collision-modes.md | 191 ++++++++++++++++++++++++++++++++++++++ DESIGN-root-tld-cert.md | 105 +++++++++++++++++++++ src/lib/resolver-web.js | 42 +++++++++ 3 files changed, 338 insertions(+) create mode 100644 DESIGN-collision-modes.md create mode 100644 DESIGN-root-tld-cert.md diff --git a/DESIGN-collision-modes.md b/DESIGN-collision-modes.md new file mode 100644 index 0000000..413ce15 --- /dev/null +++ b/DESIGN-collision-modes.md @@ -0,0 +1,191 @@ +# Design: BCNR ↔ ICANN collision modes + +**Status:** design (2026-07-30). Daemon-side hard modes βœ… built; client-side +soft mode + switcher πŸ“ to build. + +The definitive spec for how a client picks between the **Bitcoin Cash Name +Registry (BCNR)** and **ICANN** when both roots claim the same name β€” a *user +choice*, modeled on the OS "Open with…" pattern. + +## 1. The problem in one paragraph + +Two roots resolve names now: ICANN (traditional DNS) and BCNR (on-chain). Most +names live in only one root (`google.com` on ICANN, `hello.bch` on BCNR β€” no +conflict). But sometimes both claim the same name: you register `syskypo.de` on +BCNR while `syskypo.de` also exists at the German .de registry. That's a +**collision**. Somebody has to decide which one loads. This document defines who +decides, how, and where the code lives. + +## 2. Terminology + +- **BCNR** β€” Bitcoin Cash Name Registry (the on-chain registry, this project). +- **ICANN** β€” the incumbent DNS root and its registries. +- **BCNR-native TLD** β€” a TLD that BCNR treats as its own (currently `.bch`, + and any TLD the on-chain **root TLD certificate** lists). Not in the IANA root. +- **ICANN TLD** β€” a TLD in the IANA root (`.com`, `.de`, `.dev`, …). +- **Collision** β€” a name that resolves in **both** roots (only possible on an + ICANN TLD, since ICANN doesn't serve BCNR-native TLDs). +- **Hard mode** β€” the daemon picks automatically per policy, no prompting. +- **Soft mode** β€” the client prompts on collision, remembers the choice. +- **Switcher** β€” a per-tab UI to flip an already-loaded page between roots. + +## 3. The three modes + +Modes 1 and 2 are **hard**: implemented in the daemon; they always resolve +automatically. Mode 3 is **soft**: implemented in the client; it prompts on +collision (like "Open with…") and delegates non-collision cases to the daemon +underneath. + +| # | Policy | Where | Collision winner | Non-collision | +|---|---|---|---|---| +| **1** | **BCNR-priority** *(default)* | daemon | BCNR | falls through to the other root | +| **2** | **ICANN-priority** | daemon | ICANN | falls through to the other root | +| **3** | **Soft β€” Ask each time** | client (Theseus / extension) | asked, remembered per name/TLD | same as underlying hard mode | + +Env: `BNS_POLICY=bcnr-first` (default) Β· `BNS_POLICY=icann-first`. +(Alias: `bns-first` β†’ `bcnr-first`.) + +**A previous "strict / native" hard mode was removed** β€” its user-visible outputs +matched mode 2 for every real case. The only genuinely new user-facing behavior +soft brings is *prompting*, so it lives in one place: the client. + +## 4. The mental model β€” "Open with…" + +Users already know this pattern from every OS: + +| OS "Open with…" | BCNR/ICANN soft mode | +|---|---| +| One app can open the file β†’ opens automatically | No collision β†’ resolves automatically | +| Multiple apps β†’ dialog appears | Collision β†’ prompt appears | +| "Always use this app" checkbox β†’ sets a default | "Always use for this name" β†’ remembers per name | +| Change default in Settings β†’ new default sticks | Change in browser Settings β†’ new default sticks | +| Right-click β†’ "Open with…" (one-off override) | Live switcher (β›“ BCNR ↔ 🌐 ICANN) β†’ per-tab flip | + +Zero learning curve β€” build the same UX and users get it immediately. + +## 5. Granularity β€” same as file associations + +OSes let you set defaults per file *type* AND per file. The client does both, +resolved top-to-bottom on collision: + +1. **Per-name override** β€” "always use ICANN for `syskypo.de`" β€” most specific wins. +2. **Per-TLD default** β€” "always use ICANN for `.de`" β€” set once, applies to all `.de`. +3. **Global tie-breaker** β€” the hard-mode setting of the underlying daemon + (bcnr-first or icann-first). Whatever the user picked at install. + +If none of #1/#2 have an entry and mode is **soft**, prompt. Otherwise auto-resolve. + +## 6. The live switcher + +Always available in the browser chrome (Theseus natively; other browsers via the +extension). It: +- Shows a badge β€” **β›“ BCNR** or **🌐 ICANN** β€” indicating what's serving the tab. +- One click flips it. Reload with the *other* root. Reversible. +- **Offers to remember** β€” "Always use ICANN for this name? / for all .de?" β€” + which writes the per-name or per-TLD default (Β§5), so the choice persists. +- Available even in hard modes β€” a hard-mode user can still one-off override + for a specific tab without changing their global setting. + +## 7. The prompt (soft mode) + +Fires only when: mode is soft, both roots have an answer, and no per-name / +per-TLD override exists. Contents: + +``` +syskypo.de exists in two registries. + β›“ Bitcoin Cash Name Registry (BCNR) preview: + 🌐 ICANN (.de national registry) preview: real IP / cert issuer + + [ ] Always use this choice for syskypo.de + [ ] Always use this choice for all .de names + + [ Open with BCNR ] [ Open with ICANN ] [ Cancel ] +``` + +Design notes: **don't preload either page** before the choice (privacy β€” no +silent request leaks); the previews come from **cached record metadata**, not +live fetches. `[Cancel]` returns to the previous page. + +## 8. Storage β€” user choices + +Small JSON in the client's userData (Theseus) or extension storage: + +```json +{ + "collisions": { + "byName": { + "syskypo.de": "icann", + "myname.dev": "bcnr" + }, + "byTld": { + "de": "icann" + }, + "history": [ + { "name": "syskypo.de", "at": "2026-07-30T12:34:56Z", "chose": "icann" } + ] + } +} +``` + +Reset: **"Reset default registries"** (like OS "Reset default apps") wipes +`byName` and `byTld` in one action. + +## 9. Where the two lists come from (both must stay fresh) + +Collision detection needs to classify every TLD as ICANN, BCNR-native, or both: + +- **ICANN root** β†’ **IANA** (`iana-tlds.txt`, currently `D:\Dev\NameCoin\src\data\`). + A tiny file (~1400 lines), updated by IANA rarely. Refresh script runs monthly + or ships with the installer/extension update. +- **BCNR-native TLDs** β†’ the **on-chain root TLD certificate**: + **`tlds.bch`** (βœ… live on chipnet). The `tlds` record is a space-separated list + of activated TLDs; every resolver reads it via `fetchBcnrTlds()`. Adding a TLD + is one on-chain UPDATE. Full spec: [`DESIGN-root-tld-cert.md`](DESIGN-root-tld-cert.md). + Cached with a short TTL by clients. + +The two sets together define what "collision" means and let clients preempt the +soft prompt (or skip it when only one root claims the TLD). + +## 10. Where each piece lives + +| Piece | Home | Status | +|---|---|---| +| Hard modes 1 & 2 | `bnsd` daemon (`BNS_POLICY` env / `--policy` flag) | βœ… built | +| IANA root list | `D:\Dev\NameCoin\src\data\iana-tlds.txt` (+ refresh cron πŸ“) | βœ… file Β· πŸ“ refresher | +| BCNR-native TLD list (on-chain) | root TLD certificate | πŸ“ | +| Install-time mode prompt | `setup/install.ps1` β€” routing (surgical/full) + policy | πŸ“ | +| Soft mode + prompt UI | **Theseus** in-process; **extension** for other browsers | πŸ“ | +| Live switcher | Theseus chrome; extension toolbar | πŸ“ | +| Per-name / per-TLD overrides | client userData / extension storage | πŸ“ | +| Reset defaults | client Settings | πŸ“ | +| Bootstrap TLD list (before first resolve) | shipped list; refresh from on-chain root cert after startup | πŸ“ | + +## 11. Open questions + +- **The prompt's preview** β€” how much do we show without a network fetch? Cached + record type + a small "site title" hint from the on-chain record set for BCNR; + cached favicon + issuer for ICANN. If nothing cached, prompt shows only the + choice, no preview. **Never fetch before the user chooses.** +- **Uncooperative surfaces** β€” other browsers running raw against the system + daemon (no extension) get only the hard mode; that's fine and documented. Push + soft users toward Theseus or the extension. +- **Confidence in the BCNR-native list.** If the root TLD certificate is briefly + unreachable, cached list is used with a warning; if never seen, fall back to a + shipped baseline (`bch` + a small set). Never guess. +- **Per-subdomain overrides?** Probably not β€” per-name and per-TLD cover it. + Adding per-subdomain complicates the storage without a clear use case. +- **Policy roaming across devices?** Explicitly out of scope for v0 β€” local only. + +## 12. Not in scope + +- Reputation systems for either root (which name is "trustworthy"). The user + chooses; the client doesn't editorialize. +- Splitting resources across roots (fetching CSS from ICANN, HTML from BCNR). + Each page loads from one root at a time. +- Any automatic "smart" mode that guesses β€” every collision is either a per-name + override, per-TLD default, or a prompt. Never silent. + +--- + +*This document is the reference for future sessions building the installer, the +Theseus switcher, and the extension. Any deviation should update this file.* diff --git a/DESIGN-root-tld-cert.md b/DESIGN-root-tld-cert.md new file mode 100644 index 0000000..4ccdfde --- /dev/null +++ b/DESIGN-root-tld-cert.md @@ -0,0 +1,105 @@ +# 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`) + +```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): + +```js +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. The +certificate is held by the operator wallet on chipnet β€” for a real deployment +this needs to be a multisig, a covenant, or a governance body (Athenae council, +see `ROADMAP-IDEAS.md Β§4`) so the root isn't controlled by a single key. + +## What this unlocks + +1. **Multi-TLD resolution** (already live) β€” `bnsd` and Theseus accept any + `