theseus/bcnr-preload.js
Local Dev 27a1243e4d feat(theseus): consume owner-signed DNS records alongside on-chain records
Owners can now publish a signed _records.json (A/AAAA/MX/TXT/CNAME/NS)
beside their Sia content; the gateway verifies it against the current NFT
holder and serves it as GET /api/dns/<name>. Every BCDN resolution now
starts a background fetch of that answer (3 s cap, 30 s cache, seq rollback
guard) and attaches it to the entry as entry.dns. Navigation never waits
for it — on-chain h/s3/ip/p/u stay authoritative — except when a name has
no content record at all and a signed A is the only way to reach it. Only
registered names are looked up, so ICANN hosts never reach the gateway.

Exposed as window.bcnr.dnsRecords(name) for add-ons (TXT verification, MX
for mail bridges), on resolveName() as .dns, and as a "Signed DNS" row in
the site-info popover.
2026-09-16 00:53:13 +02:00

32 lines
2 KiB
JavaScript

// bcnr-preload.js — session-wide preload that installs `window.bcnr` on every
// page (regular tabs, popups, chrome/settings/etc). Reads only — no signing,
// no wallet unlock, no permission prompts. These four methods query the same
// resolver Theseus already runs for its address bar; nothing about the local
// user leaks, so no origin gate is needed for this surface.
//
// Sequencing: registered via `session.defaultSession.setPreloads([...])` in
// main.js at whenReady, which runs BEFORE per-WebContentsView preloads (home,
// settings, popover, etc.), so those preloads still install their own bridges
// on top of `window.bcnr`. See DESIGN-integrated-wallet.md §3 for the full
// API surface.
const { contextBridge, ipcRenderer } = require("electron");
// Every method returns a Promise; a name that fails to resolve or isn't
// registered comes back as `null` (not an error) so page code can treat
// "no such name" as data, not an exception. `getBcnrTlds` always returns
// an array — even the seed ["bch"] before the on-chain list has landed.
contextBridge.exposeInMainWorld("bcnr", {
resolveName: (name) => ipcRenderer.invoke("bcnr:resolveName", name),
isRegistered: (name) => ipcRenderer.invoke("bcnr:isRegistered", name),
getBcnrTlds: () => ipcRenderer.invoke("bcnr:getBcnrTlds"),
getRecordVersion: (name) => ipcRenderer.invoke("bcnr:getRecordVersion", name),
// Owner-signed DNS records (A/AAAA/MX/TXT/CNAME/NS) published beside the
// name's Sia content and verified by the gateway against the current NFT
// holder. `{ name, dns, seq, updatedAt, owner }`, or null when the name is
// unregistered or has published no manifest. Waits ≤ 3 s for a fetch.
dnsRecords: (name) => ipcRenderer.invoke("bcnr:dnsRecords", name),
// Diagnostic — the eTLD+1 permission origin Theseus computes for THIS page.
// dApp devs use this to see how their subdomains bucket under one grant.
// Returns null for opaque origins (data:, blob:) which never hold grants.
getOrigin: () => ipcRenderer.invoke("bcnr:getOrigin"),
});