192 lines
8.7 KiB
Markdown
192 lines
8.7 KiB
Markdown
|
|
# 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: <domain-owner label>
|
||
|
|
🌐 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.*
|