blocklist-tx.js builds the three payloads and a transaction that takes its
inputs from the list address and returns its change there, which is both the
authority proof and the funding of the next entry. blocklist-admin.mjs
publishes a report and chains the flag on its change, so the pair needs no
waiting; it refuses free-text reasons and declines 'stolen' in this phase.
bns.js recognises the opcodes and reduces a list through the shared reducer
rather than a second copy of the rules.
The tests cover the validity rules, the authority rule against a stranger who
only pays the address, ordering inside one block, and the contract that
matters most: no subscription, or a subscription to an empty list, changes
no answer.
buildReleaseTx spends the certificate and writes no token output: the
omission is the burn. The certificate's own dust pays the beacon and the fee,
so a release needs no other coin.
releaseWithBuiltInWallet checks whether the certificate sits in a beacon
transaction. One that arrived by a plain transfer cannot be proven burned
from the beacon history, so the flow publishes an UPD first and releases its
output. Both paths ran on chipnet, and a released name was registered again
after its release confirmed.
checkAvailability now reports a pending release as not available: a REG
broadcast while the release is unconfirmed could be mined beside it and be
void, and the buyer would have paid for nothing.
The CLI resolver reduces the beacon history on its own, so a rule that lives
only in resolver-web.js would be a consensus split between the tools that
register names and the clients that resolve them.
release.test.mjs feeds the same synthetic histories to both reducers and
requires identical results: valid and invalid burns, the unanchored
certificate, same-block ordering, the one-block gap in and out of the
mempool, and that nothing of a released registration carries over.
Until now a subdomain served the folder its labels spelled out, and that was
the only thing it could ever do. There was no way to point one somewhere
else, send one to another site, or stop one answering — the mapping was a
convention with no override.
A verified hosts entry now decides: a different folder, a redirect, or
nothing at all. Block beats redirect beats path, so a host that is both
blocked and redirected stays blocked. The manifest is fetched only when the
request is actually for a subdomain, and the result — including the absence
of one — is cached for a minute, so the apex pays nothing and a name that
never published one pays one lookup per minute rather than one per request.
A manifest that fails to verify is treated exactly like no manifest: the
name keeps serving from its chain records instead of failing shut.
The folder derivation moves next to the picking rule and grows tests,
because getting it wrong does not raise an error — it quietly serves the
wrong bytes. The subtle parts are that labels reverse (DNS reads outward, a
path reads inward) and that a .bch name indexes as <label>.bch even though
normalizeName collapses it to a bare label; matching the bare label would
have served the bucket root for every subdomain of every .bch name.
/api/dns answers for a host too. A host with its own dns block owns its
record set outright, the way classic DNS works; one without inherits the
apex, the way everything else on BNS does.
The signature check was written out twice — once on the read path, once on
the write path — and a third copy was about to be needed for serving. Three
hand-copies of a verification routine is how a hole gets in, and the
dangerous half is subtle: pubkey recovery always succeeds and always yields
some key, so the line that actually proves anything is the comparison of the
derived address to the on-chain owner. That line is now in one place.
The manifest schema gains a hosts block for per-subdomain records and
routing, validated strictly here because whatever we accept we then serve
under the owner's signature: no path traversal, no non-http redirect
targets, no host key that is not a label path. v1 blobs stay valid, so
nothing anyone has published needs republishing.
Also adds the rule for which hosts entry governs a request. The apex is
deliberately never one of them — its content comes from the chain, and
letting an off-chain blob redirect the main site would move a name's trust
boundary without the owner spending a transaction.
New record kind alongside h / s3 / ip / u: `p` reverse-proxies the
request to a full upstream URL while keeping the BNS host in the
address bar. Unlike `ip` (which pins the upstream to a raw IP + on-chain
TLS fingerprint), `p` uses the upstream's own DNS + public CA cert and
sends `Host:` of the upstream so vhost-based origins answer correctly.
Argus (record-picker.js):
- Placed AFTER `ip` in the apex chain: a name carrying both keeps its
existing pinned-IP behavior; names with only `p` get honored instead
of falling through to `u` and 302-ing away.
- Not applied to subdomain inheritance — `p` is single-URL by intent,
and inheriting through a subdomain would silently mangle the target.
Theseus (main.js serveBns):
- Fetches the upstream (path prefix from the `p` URL is preserved) and
returns the response body/status/content-type verbatim. Provenance
label "mirror" appears in the source badge.
Tests: record-picker.test.mjs covers `p`-only, `p`+ip precedence, and
the subdomain-suppression rule.
Per-TLD certificates on a distinct TLD beacon supersede the legacy list-in-one-NFT
model. TLD label is the NFT commitment; TREG/TUPD payloads on the TLD beacon
mirror REG/UPD on the name beacon. Chipnet TLD beacon:
bchtest:qzc4c76vtw9nd49g6x8nmvc0jz62uvk4dc7hmpmerh
scripthash c85db10e106606273422b2733b2a6fb8b35a4d0e7dbfb20cf59bdb0833c008b8
* config.js, resolver-web.{js,d.ts} — TLD_BEACON + TLD_BEACON_SCRIPTHASH
* bns.js — KNOWN_OPS { REG, UPD, TREG, TUPD }, registerTld/updateTld/normalizeTld
* bns.js + resolver-web.js — buildIndex({enforceTld:false}) drops name REGs
whose TLD lacks a live TLD cert at their block height (mempool-friendly).
Lockstep across both files.
* lib/tld-index.js — buildTldSnapshot({history,txs}) → deterministic root over
canonicalised tlds[]. Pure, importable, and stable across insertion order.
* indexer/bns-indexer.js — BeaconIndex is multi-beacon; ws handler dispatches
by scripthash; new HTTP endpoint GET /tld-list.json + /health behind
BNS_HTTP_PORT; state file v1 → v2 migrates in fromJSON.
* publish-tld-mirror.mjs — S3 (floating + immutable content-addressed) plus
NIP-33 kind 30078 d:bns-tld-list to Nostr relays, signed via nostr-tools
(libauth's Schnorr differs from BIP-340 and relays reject it).
* seed-tld-beacon.mjs — idempotent chipnet seed. Current set on the beacon:
bch p2p bit nav test x asm neo gt sc sia com dex cex nt (15). `.com` is
held by the operator: cert on-chain, excluded from public registrar UI.
Design + docs:
* Decentralized.DNS/DESIGN-tld-registry.md — schema, enforcement,
operator-countersig gate, USD-tiered pricing floors, fee splits,
tracker/mirror mechanics, migration/grandfathering.
* INSTRUCTIONS.md §3 rewritten for the 4-step flow (TREG → UI → deploy →
republish snapshot).
* site/tlds/ — public docs page listing current TLDs + verification paths.
* site/sirius/register.html — dropdown removed; parallel search across
every TLD in the TLDS array, sorted available → taken → not-on-registry.
Config:
* ariadne.config.json tlds → [bch, p2p, bit, nav, test, x] (CA constraints;
on-chain registry is now the authoritative source, config is fallback).
* setup/install.ps1 — reads TLD list via fetchTldList (per-TLD registry)
with fallback to fetchBcnrTlds (legacy tls.bch list-NFT) then config.
Tests:
* test/tld-index.test.mjs — 30 self-contained checks (multi-beacon dispatch,
state migration, ws routing, snapshot shape + root recomputability).
* test/tld-enforcement.test.mjs — synthetic + live chipnet integration
(with-gate correctly drops all pre-seed names).
Root cause of the "Firefox 'Server Not Found' (github)" and Node
`getaddrinfo EAI_FAIL` reports against the shipped daemon: reply() in
bnsd.js built responses from scratch and never copied the query's EDNS0
OPT record from the additional section. RFC 6891 §6.1.1 requires the
response to echo OPT when the query carried one; strict clients (glibc
getaddrinfo, Firefox with TRR off) treat the missing OPT as FORMERR and
surface EAI_FAIL / "Server Not Found". Lenient clients (nslookup, Chrome
async-dns) tolerate the loss, which is why the bug looked flaky.
The prior commit (ef19db1) extracted src/lib/dns-server.js as a testable
surface but left bnsd.js still calling the old inline helpers. This
finishes the migration:
- src/lib/dns-server.js:
- buildReply() now walks the additional section, finds the OPT RR by
name=root/type=41, and appends it to the response. When the query
had no OPT, none is fabricated (RFC 6891 §7). Preserves RD and
opcode from the query rather than hardcoding 0x8580.
- parseQuestion() rejects QDCOUNT != 1 and truncated labels early,
and skipName() tolerates DNS compression pointers per RFC 1035
§4.1.4 so the OPT walker doesn't trip on a compressed name.
- forwardQuery() accepts "host" (default port 53) or "host:port" so
it can be tested off port 53.
- src/daemon/bnsd.js: replaces the inline helpers with imports; the
icann-first branch keeps its rcode-inspecting callback via a small
forwardInspect() wrapper around the Promise-based forwardQuery.
- test/dns-server.test.mjs (new): 14 tests. Includes parseQuestion
cases (plain / EDNS / bad QDCOUNT / truncated), buildReply invariants
(OPT echoed / OPT never fabricated / RD preserved / NODATA still
echoes OPT), full dns.Resolver round-trips against an in-process
server (A / AAAA-NODATA / forward-to-mock-upstream), and two pinned
regression tests that copy the OLD reply() verbatim and assert its
OPT-dropping behavior so a future regression is caught immediately.
- AriadneResolver/ariadne-resolver/{src/daemon/bnsd.js, src/lib/dns-server.js}:
synced from Argus so the packaged installer picks up the fix.
Pre-existing failures in test/portfolio-index.test.mjs (require is not
defined at src/lib/chain.js:54) are unrelated and predate this work.
Theseus Navigator:
- Firefox-style toolbar: SVG back/fwd/reload/home buttons
- security padlock badge in the address bar opens a floating site-info
overlay VIEW (on top of the page, never pushes content down)
- Edge-style favorites bar (shown only on the new-tab/home page)
- search-engine picker (DuckDuckGo/Google/Bing/Brave/Startpage)
- Theseus logo moved right, opens Settings; native menu removed
- address bar doubles as search; bottom resolver status line
Argus:
- BNS-only indexer: an electrum endpoint over a bare BCHN node
(watches just the beacon, caches txs so no txindex needed) + test
- registry lifecycle / portfolio / name-index modules
Site restructure (nav/apps/store/hermes/choose) + runbook notes.