# 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.*