2026-07-30 20:58:45 +02:00
# BNS resolver daemon installer -- run from an ELEVATED PowerShell.
2026-07-29 13:54:34 +02:00
# .\setup\install.ps1 # surgical: NRPT rule routes only .bch to the daemon
# .\setup\install.ps1 -Mode full # universal: adapter DNS -> 127.0.0.1, daemon forwards the rest
param (
[ ValidateSet ( " nrpt " , " full " ) ] [ string ] $Mode = " nrpt " ,
[ string ] $Upstream = " 1.1.1.1 " ,
2026-08-01 02:06:48 +02:00
# Collision policy the daemon should run under (docs: START-HERE.md 4b).
# bcnr-first (default): BCNR wins, ICANN fallback on miss.
# icann-first : ICANN wins, BCNR fallback on NXDOMAIN.
# bns-first is accepted as an alias for bcnr-first.
[ ValidateSet ( " bcnr-first " , " icann-first " , " bns-first " ) ] [ string ] $Policy = " bcnr-first " ,
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
# BNS indexer(s) for the primary TLD discovery source. Comma-separated list
# so we can fall over between mirrors -- HTTPS beats WSS-electrum from a
# silent-install context (no Node subprocess, no ws dependency, DPI-friendly).
# The first indexer that answers within its per-source deadline wins.
[ string ] $Indexer = " https://navigate.st,https://silentmode.st " ,
2026-07-29 13:54:34 +02:00
# Opt-in: turn off Firefox DNS-over-HTTPS. Only needed to resolve names that
2026-07-30 20:58:45 +02:00
# ALSO exist in ICANN (a reclaimed .de) via BCNR in Firefox -- BCNR-only names
2026-07-29 13:54:34 +02:00
# (all .bch, etc.) already work with DoH on, because DoH returns NXDOMAIN and
# Firefox falls back to the local resolver. Trade-off: Firefox's other DNS then
# goes through the system resolver instead of encrypted to Cloudflare.
[ switch ] $FirefoxNoDoH ,
# Opt-in: force-close a running Firefox so it reloads the new root CA + policy
# immediately (Firefox only reads them at startup). Off by default because it
# discards the user's open tabs/unsaved data.
[ switch ] $CloseFirefox
)
$ErrorActionPreference = " Stop "
2026-09-08 01:27:51 +02:00
# ---- persistent install log --------------------------------------------------
# Silent-install callers (Theseus's NSIS chain-install, `.exe /VERYSILENT`, Inno
# `runhidden`) discard our stdout/stderr, so historically any failure between
# "files copied" and "scheduled task registered" was invisible. This appends a
# timestamped transcript to a well-known path so a broken install always leaves
# forensics behind. Errors starting the transcript are non-fatal: an unloggable
# install must still complete or fail visibly.
$LogDir = Join-Path $env:ProgramData 'Ariadne'
try { New-Item -ItemType Directory -Force -Path $LogDir | Out-Null } catch { }
$LogFile = Join-Path $LogDir 'install.log'
$ErrFile = Join-Path $LogDir 'install-error.log'
try { Start-Transcript -Path $LogFile -Append -Force -IncludeInvocationHeader | Out-Null } catch { }
Write-Host " "
Write-Host ( " ==== install.ps1 " + ( Get-Date -Format 's' ) + " Mode= $Mode Policy= $Policy Indexer= $Indexer ==== " )
# Script-scope trap: any uncaught exception is written to install-error.log
# (separate file so a post-mortem doesn't have to grep through the whole
# transcript), the transcript is closed cleanly, then the exception is
# re-thrown so the caller (Inno's [Run], NSIS's ExecWait, or the user's PS)
# still sees a non-zero exit code and can act on it.
trap {
try {
$stamp = Get-Date -Format 's'
$msg = " [FATAL $stamp ] $( $_ | Out-String ) `n ScriptStack: `n $( $_ . ScriptStackTrace ) `n "
Add-Content -Path $ErrFile -Value $msg -ErrorAction SilentlyContinue
Write-Host $msg -ForegroundColor Red
} catch { }
try { Stop-Transcript | Out-Null } catch { }
exit 1
}
2026-07-29 13:54:34 +02:00
if ( -not ( [ Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity ] :: GetCurrent ( )
) . IsInRole ( [ Security.Principal.WindowsBuiltInRole ] :: Administrator ) ) {
2026-09-08 01:27:51 +02:00
try { Stop-Transcript | Out-Null } catch { }
2026-07-29 13:54:34 +02:00
throw " Run this from an elevated (Administrator) PowerShell. "
}
$root = Split-Path -Parent $PSScriptRoot
# Prefer a bundled Node runtime (distributable installer ships runtime\node.exe);
# fall back to a Node on PATH for running straight from the dev repo.
$node = Join-Path $root " runtime\node.exe "
if ( -not ( Test-Path $node ) ) {
$onPath = Get-Command node -ErrorAction SilentlyContinue
if ( -not $onPath ) { throw " No Node runtime found: expected runtime\node.exe (bundle) or node on PATH (dev repo). " }
$node = $onPath . Source
}
$daemon = Join-Path $root " src\daemon\bnsd.js "
$stateFile = Join-Path $PSScriptRoot " install-state.json "
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
# TLS 1.2 (PS 5.1 defaults to 1.0/1.1 on older boxes and Invoke-RestMethod fails
# against modern servers).
try { [ Net.ServicePointManager ] :: SecurityProtocol = [ Net.ServicePointManager ] :: SecurityProtocol -bor [ Net.SecurityProtocolType ] :: Tls12 } catch { }
2026-09-08 01:27:51 +02:00
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
# --- helper: run `node --input-type=module -e "<script>"` and capture output
# via Windows-level stream redirection so PS 5.1's "$ErrorActionPreference=Stop
# + native stderr = NativeCommandError" quirk cannot kill us. That quirk is
# what silently broke 0.1.0/0.1.1 install.ps1 at the CA-rotation probe.
function Invoke-NodeCapture([string]$Script ) {
$tempOut = [ System.IO.Path ] :: GetTempFileName ( )
$tempErr = [ System.IO.Path ] :: GetTempFileName ( )
try {
$p = Start-Process -FilePath $node -ArgumentList @ ( '--input-type=module' , '-e' , $Script ) `
-WorkingDirectory $root -Wait -PassThru -NoNewWindow `
-RedirectStandardOutput $tempOut -RedirectStandardError $tempErr
return [ pscustomobject ] @ {
ExitCode = $p . ExitCode
StdOut = if ( Test-Path $tempOut ) { Get-Content $tempOut -Raw } else { " " }
StdErr = if ( Test-Path $tempErr ) { Get-Content $tempErr -Raw } else { " " }
}
} finally {
Remove-Item $tempOut , $tempErr -Force -ErrorAction SilentlyContinue
2026-09-08 01:27:51 +02:00
}
}
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
# Discover the set of currently activated BCNR TLDs. Sources, in order of
# preference (first hit wins, tight per-source deadline):
# 1) BNS indexer(s) HTTPS GET <indexer>/api/tlds -> {advertised:[...]}
# Fast + reliable when up; -Indexer takes a comma-separated list so the
# installer can fall over between mirrors (default: navigate.st and
# silentmode.st, both live on the same VPS today; add your own to widen).
# 2) Local cache from a previous successful indexer/electrum fetch
# (C:\ProgramData\Ariadne\tlds-cache.json). Instant; always right on any
# machine that has ever successfully installed before. May be stale --
# won't have TLDs added since last install -- but never wrong.
# 3) On-chain via electrum WSS (per-TLD beacon fetchTldList; legacy
# fetchBcnrTlds as sub-fallback). Slower + WSS-fragile.
# 4) Bundled ariadne.config.json snapshot (frozen at installer build time).
# 5) Bootstrap set [bch] -- last resort so the daemon still starts.
# Cache is refreshed whenever an authoritative source (indexer or electrum)
# succeeds. This drives both the CA's nameConstraints extension and the
# surgical-mode NRPT rules below; adding a new TLD on-chain is picked up by
# re-running this installer (no code release).
$tldCachePath = Join-Path $LogDir 'tlds-cache.json'
2026-09-08 01:27:51 +02:00
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
function Fetch-TldsFromIndexer([string]$IndexerUrl ) {
try {
$url = $IndexerUrl . TrimEnd ( '/' ) + " /api/tlds "
Write-Host " [..] indexer: $url "
$r = Invoke-RestMethod -Uri $url -TimeoutSec 5 -UseBasicParsing -Headers @ { 'User-Agent' = 'AriadneResolverInstaller/1' }
if ( $r -and $r . advertised -and $r . advertised . Count -gt 0 ) {
return @ { tlds = @ ( $r . advertised | ForEach-Object { [ string ] $_ } ) ; source = " indexer( $IndexerUrl ) " }
}
} catch { Write-Host " [!] $IndexerUrl -> $( $_ . Exception . Message ) " -ForegroundColor Yellow }
return $null
}
function Fetch-TldsFromCache([string]$Path ) {
if ( -not ( Test-Path $Path ) ) { return $null }
BNS: per-TLD on-chain registry, multi-beacon indexer, enforcement gate
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).
2026-08-29 17:11:02 +02:00
try {
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
$c = Get-Content $Path -Raw | ConvertFrom-Json
if ( $c . tlds -and $c . tlds . Count -gt 0 ) {
$age = try { [ int ] ( ( Get-Date ) - ( Get-Date $c . savedAt ) ) . TotalDays } catch { -1 }
return @ { tlds = @ ( $c . tlds | ForEach-Object { [ string ] $_ } ) ; source = " cache(age= ${age} d) " }
BNS: per-TLD on-chain registry, multi-beacon indexer, enforcement gate
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).
2026-08-29 17:11:02 +02:00
}
} catch { }
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
return $null
}
function Fetch-TldsFromElectrum {
$js = " import('./src/lib/resolver-web.js').then(async m=>{const ws=(await import('ws')).default;try{const list=await m.fetchTldList({WebSocket:ws});if(list&&list.length&&!(list.length===1&&list[0]==='bch')){console.log(JSON.stringify(list));return}}catch(e){}try{const legacy=await m.fetchBcnrTlds({WebSocket:ws});console.log(JSON.stringify(legacy||[]))}catch(e){console.log('[]')}}).catch(e=>console.log('[]')) "
$r = Invoke-NodeCapture $js
try {
$arr = $r . StdOut . Trim ( ) | ConvertFrom-Json
if ( $arr -and $arr . Count -gt 0 ) { return @ { tlds = @ ( $arr | ForEach-Object { [ string ] $_ } ) ; source = " electrum " } }
} catch { }
return $null
2026-08-01 02:06:48 +02:00
}
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
function Fetch-TldsFromConfig([string]$CfgPath ) {
try {
$c = Get-Content $CfgPath -Raw | ConvertFrom-Json
if ( $c . tlds -and $c . tlds . Count -gt 0 ) {
return @ { tlds = @ ( $c . tlds | ForEach-Object { [ string ] $_ } ) ; source = " ariadne.config.json " }
}
} catch { }
return $null
}
$result = $null
foreach ( $ix in ( $Indexer -split ',' | ForEach-Object { $_ . Trim ( ) } | Where-Object { $_ } ) ) {
$result = Fetch-TldsFromIndexer $ix
if ( $result ) { break }
}
if ( -not $result ) { $result = Fetch-TldsFromCache $tldCachePath ; if ( $result ) { Write-Host " [!] no indexer reachable -- using $( $result . source ) " -ForegroundColor Yellow } }
if ( -not $result ) { $result = Fetch-TldsFromElectrum ; if ( $result ) { Write-Host " [ok] resolved via electrum WSS " } }
if ( -not $result ) { $result = Fetch-TldsFromConfig ( Join-Path $root " ariadne.config.json " ) ; if ( $result ) { Write-Host " [!] falling back to bundled snapshot " -ForegroundColor Yellow } }
if ( -not $result ) { Write-Host " [!] no TLD source available -- bootstrap [bch] " -ForegroundColor Yellow ; $result = @ { tlds = @ ( 'bch' ) ; source = 'bootstrap' } }
2026-08-01 02:06:48 +02:00
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
$ACTIVATED_TLDS = $result . tlds
Write-Host " [ok] activated TLDs (source: $( $result . source ) ): $( $ACTIVATED_TLDS -join ', ' ) "
# Persist cache on any authoritative fetch. Cache and bootstrap don't refresh
# themselves; only network sources do.
if ( $result . source -like 'indexer(*)' -or $result . source -eq 'electrum' ) {
try {
@ { tlds = $ACTIVATED_TLDS ; savedAt = ( Get-Date -Format 's' ) ; source = $result . source } |
ConvertTo-Json | Set-Content -Path $tldCachePath -Encoding utf8
Write-Host " [ok] cached TLD list to $tldCachePath "
} catch { Write-Host " [!] could not write cache: $( $_ . Exception . Message ) " -ForegroundColor Yellow }
}
# Generate the name-constrained local root CA now (on THIS machine; the key
# never ships). If ca/ exists but its permitted-TLD set no longer matches, we
# rotate: delete the stale CA so loadOrCreateRootCA writes a fresh one with
# current constraints. Otherwise clients would reject leaves for any
# newly-added TLD.
2026-08-01 02:06:48 +02:00
$caCrt = Join-Path $root " ca\root-ca.crt "
$caKey = Join-Path $root " ca\root-ca.key "
$tldsArg = ( $ACTIVATED_TLDS | ForEach-Object { " ' " + ( $_ -replace " ' " , " '' " ) + " ' " } ) -join " , "
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
2026-08-01 02:06:48 +02:00
if ( Test-Path $caCrt ) {
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
$r = Invoke-NodeCapture " import('./src/lib/ca.js').then(m=>{try{m.loadOrCreateRootCA([ $tldsArg ]);console.log('ok')}catch(e){console.log('err')}}).catch(e=>console.log('err')) "
# ca.js prints the constraint-mismatch phrase to stderr; we grep StdErr here
# instead of merging streams (which is what tripped PS 5.1 before).
if ( $r . StdErr -match 'is constrained to \[.*\] but the requested TLD list is' ) {
Write-Host " [ok] rotating CA to match current TLD set "
2026-08-01 02:06:48 +02:00
Remove-Item -Path $caCrt , $caKey -Force -ErrorAction SilentlyContinue
}
}
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
$r = Invoke-NodeCapture " import('./src/lib/ca.js').then(m=>m.loadOrCreateRootCA([ $tldsArg ])).catch(e=>{console.error(String(e));process.exit(1)}) "
if ( $r . ExitCode -ne 0 ) {
if ( $r . StdErr ) { Write-Host $r . StdErr -ForegroundColor Red }
throw " CA generation failed (node exit $( $r . ExitCode ) ) "
}
if ( -not ( Test-Path $caCrt ) ) { throw " CA generation produced no root-ca.crt " }
Write-Host " [ok] CA at $caCrt "
2026-07-29 13:54:34 +02:00
2026-08-01 02:06:48 +02:00
# 0) Idempotency: clear every previous NRPT rule we own (any TLD).
Get-DnsClientNrptRule | Where-Object { $_ . Comment -like 'BNS *resolver' } | ForEach-Object {
2026-07-29 13:54:34 +02:00
Remove-DnsClientNrptRule -Name $_ . Name -Force
}
try { Stop-ScheduledTask -TaskName " BNS Resolver Daemon " -ErrorAction Stop } catch { }
try { Stop-ScheduledTask -TaskName " BNS Sia Bridge " -ErrorAction Stop } catch { }
# 1) Scheduled task: run the daemon at boot as SYSTEM (survives logoff, binds port 53/80/443)
2026-08-01 02:06:48 +02:00
# Pass --policy so bnsd.js applies the chosen collision mode (docs: 4b).
$action = New-ScheduledTaskAction -Execute $node -Argument " `" $daemon `" --upstream $Upstream --policy $Policy " -WorkingDirectory $root
2026-07-29 13:54:34 +02:00
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -RestartCount 5 -RestartInterval ( New-TimeSpan -Minutes 1 ) -ExecutionTimeLimit ( New-TimeSpan -Days 3650 )
Register-ScheduledTask -TaskName " BNS Resolver Daemon " -Action $action -Trigger $trigger -Settings $settings `
-User " SYSTEM " -RunLevel Highest -Force | Out-Null
Start-ScheduledTask -TaskName " BNS Resolver Daemon "
Write-Host " [ok] daemon installed as scheduled task 'BNS Resolver Daemon' and started "
# 1b) Sia bridge (optional): serves "s3" records from the Sia network
$s3d = Join-Path $root " tools\s3d.exe "
if ( Test-Path $s3d ) {
# runs as the logged-on user (s3d's login state lives in the user's APPDATA)
$s3dAction = New-ScheduledTaskAction -Execute $s3d -WorkingDirectory $root
$s3dTrigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME
$s3dPrincipal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive
Register-ScheduledTask -TaskName " BNS Sia Bridge " -Action $s3dAction -Trigger $s3dTrigger `
-Settings $settings -Principal $s3dPrincipal -Force | Out-Null
Start-ScheduledTask -TaskName " BNS Sia Bridge "
Write-Host " [ok] s3d Sia bridge installed (runs at your logon) and started "
}
# 2) Route queries to it
$state = @ { mode = $Mode ; adapters = @ ( ) }
if ( $Mode -eq " nrpt " ) {
2026-08-01 02:06:48 +02:00
# Surgical mode: one NRPT rule per TLD that tlds.bch has activated on-chain.
# BCNR-native TLDs are declared there and only there; a re-install after a new
# TLD is activated picks it up with no code release. Dual TLDs that collide
# with ICANN are still safe to route because the daemon falls through to
# UPSTREAM on any BCNR miss.
foreach ( $tld in $ACTIVATED_TLDS ) {
$ns = " . $tld "
# Idempotent: clean any pre-existing rule for this namespace first.
Get-DnsClientNrptRule | Where-Object { $_ . Namespace -contains $ns -and $_ . Comment -like 'BNS *' } | ForEach-Object {
Remove-DnsClientNrptRule -Name $_ . Name -Force
}
Add-DnsClientNrptRule -Namespace $ns -NameServers " 127.0.0.1 " -Comment " BNS $ns resolver "
Write-Host " [ok] NRPT rule added: $ns -> 127.0.0.1 "
2026-07-31 08:43:31 +02:00
}
Write-Host " [ok] all other DNS untouched "
2026-07-29 13:54:34 +02:00
} else {
# Reinstall-safe backup: if the machine is ALREADY pointed at us (127.0.0.1) from a
# prior install, the "current" servers are a useless restore target. Reuse the last
# good backup if we have one, else fall back to DHCP. NEVER record loopback as the
2026-07-30 20:58:45 +02:00
# original -- that's exactly what left a past uninstall pointing DNS at a dead daemon.
2026-07-29 13:54:34 +02:00
$prior = @ { }
if ( Test-Path $stateFile ) {
try { ( Get-Content $stateFile | ConvertFrom-Json ) . adapters | ForEach-Object { $prior [ $_ . alias ] = $_ } } catch { }
}
foreach ( $if in Get-DnsClientServerAddress -AddressFamily IPv4 |
Where-Object { $_ . InterfaceAlias -notmatch " Loopback " -and $_ . ServerAddresses . Count -gt 0 } ) {
$orig = @ ( $if . ServerAddresses | Where-Object { $_ -ne '127.0.0.1' -and $_ -ne '::1' } )
if ( $orig . Count -eq 0 -and $prior . ContainsKey ( $if . InterfaceAlias ) ) {
$orig = @ ( $prior [ $if . InterfaceAlias ] . servers | Where-Object { $_ -and $_ -ne '127.0.0.1' -and $_ -ne '::1' } )
}
$state . adapters + = @ { alias = $if . InterfaceAlias ; servers = $orig ; resetToDhcp = ( $orig . Count -eq 0 ) }
Set-DnsClientServerAddress -InterfaceAlias $if . InterfaceAlias -ServerAddresses 127.0 . 0 . 1
$restoreTo = if ( $orig . Count ) { $orig -join ', ' } else { 'DHCP (automatic)' }
Write-Host " [ok] adapter ' $( $if . InterfaceAlias ) ' DNS -> 127.0.0.1 (restore target: $restoreTo ) "
}
}
# 3) TLS trust anchor: BNS Local Root CA into the machine store, and tell
# Firefox (which keeps its own store) to honor Windows enterprise roots.
$caCert = Join-Path $root " ca\root-ca.crt "
if ( Test-Path $caCert ) {
# Refuse to trust a root that can vouch for the whole internet. Roots generated
# before 2026-07-26 carry no name constraints; installing one means anyone who
# can read ca\root-ca.key can forge a trusted certificate for any domain on this
# machine. Constrained roots are limited to the TLDs in src\lib\tlds.js.
$ncOid = " 2.5.29.30 "
$parsed = [ System.Security.Cryptography.X509Certificates.X509Certificate2 ] :: new ( $caCert )
$hasConstraints = @ ( $parsed . Extensions | Where-Object { $_ . Oid . Value -eq $ncOid } ) . Count -gt 0
$parsed . Dispose ( )
if ( -not $hasConstraints ) {
Write-Host " "
Write-Host " [STOP] ca\root-ca.crt has NO X.509 name constraints. " -ForegroundColor Red
Write-Host " Trusting it would let anyone who reads ca\root-ca.key forge a " -ForegroundColor Red
Write-Host " certificate for ANY domain on this machine, not just BNS names. " -ForegroundColor Red
Write-Host " Rotate the CA, then re-run this installer: " -ForegroundColor Red
Write-Host " setup\uninstall.ps1 (removes the old root from the store) " -ForegroundColor Red
Write-Host " Remove-Item -Recurse $( $root ) \ca "
Write-Host " setup\install.ps1 (generates a constrained root) " -ForegroundColor Red
Write-Host " "
throw " refusing to install an unconstrained root CA "
}
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
# .NET direct -- Import-Certificate depends on the Cert: PSDrive which is
# not always loaded in -NoProfile elevated shells (Inno's [Run] context, for
# example). Adding to X509Store('Root','LocalMachine') is idempotent.
$store = New-Object System . Security . Cryptography . X509Certificates . X509Store ( 'Root' , 'LocalMachine' )
$store . Open ( 'ReadWrite' )
$imported = New-Object System . Security . Cryptography . X509Certificates . X509Certificate2 ( $caCert )
Ariadne 0.1.4: sweep stale BNS root CAs so browsers stop failing after upgrades
Symptom: on any machine that had been through a series of installs, one or
more older "BNS Local Root CA" certs from earlier builds could accumulate in
LocalMachine\Root -- an unconstrained root from before 2026-07, or a rotated
one -- and every install added the current constrained root ALONGSIDE the
stale ones. Two certs with the same Subject-DN defeat browser chain-builders:
Chromium/Edge/Firefox pick one candidate and refuse to try the others, so
every .bch site failed with ERR_CERT_AUTHORITY_INVALID / SEC_ERROR_BAD_SIGNATURE
in every browser EXCEPT Theseus (which trusts the on-chain tls fingerprint
directly, not the machine store).
Reproduced on the dev box: two BNS Local Root CAs present (thumbprints
EB5DBE98... constrained + 23538FD7... unconstrained), DNS answered correctly,
strict Invoke-WebRequest to https://coinspectrum.bch/ failed. Removing the
stale one and re-running the chain-build restored HTTPS 200.
Fix (install.ps1):
- Before Add(imported), enumerate the store and $store.Remove() every
"BNS Local Root CA" whose thumbprint differs from the one we're about
to trust. Post-condition: exactly one BNS root remains after install.
Fix (uninstall.ps1):
- Switched from `Get-ChildItem Cert:\LocalMachine\Root` to X509Store direct.
The Cert: PSDrive is not always loaded in Inno's [UninstallRun] -NoProfile
context, so uninstall could silently leave the CA behind on the store side
even while it removed everything else.
Verified end-to-end on this box: 0.1.4 upgrade over 0.1.3 finished cleanly,
BNS Resolver Daemon Running, 127.0.0.1:53 open, exactly one BNS Local Root
CA in the machine store, strict HTTPS to https://coinspectrum.bch/ returned
200 (506,997 bytes) with X509Chain.Build ok=true leaf -> BNS root.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.4.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.4.exe
- silentmode.st manifest advertises 0.1.4
- sha256 30ed5e3bb6d6da5325f854c97976410b4fc63067ed52c810a55e9d2e1bdc4e17
2026-09-27 22:04:00 +02:00
# Sweep any prior "BNS Local Root CA" whose thumbprint differs from the one
# we're about to trust. Two roots with the same Subject-DN confuse browsers'
# chain-builders (Chromium/Edge/Firefox all pick one candidate and refuse to
# try others), which manifests as "resolver installed and DNS answers, but
# every .bch site fails with SEC_ERROR_BAD_SIGNATURE / ERR_CERT_AUTHORITY_INVALID
# in every browser except Theseus". A stale unconstrained root left over from
# an early build is enough to trigger it -- and there's no way to detect it
# without walking the store, because both roots claim the same identity.
# Post-condition: exactly one "BNS Local Root CA" remains after this block.
foreach ( $c in @ ( $store . Certificates | Where-Object { $_ . Subject -match 'BNS Local Root CA' -and $_ . Thumbprint -ne $imported . Thumbprint } ) ) {
Write-Host " [ok] removing stale BNS root CA ( $( $c . Thumbprint ) , NotAfter $( $c . NotAfter ) ) "
$store . Remove ( $c )
}
Ariadne 0.1.2: install.ps1 no longer dies on PS 5.1 stderr quirk; layered TLD-source cascade
install.ps1 root-cause fix
- Every `& $node ... 2>&1` had been tripping PS 5.1's "$ErrorActionPreference=Stop +
native-command stderr = terminating error" quirk. Trap-and-log added in 0.1.1 EXPOSED
the bug (0.1.0 died at the exact same line -- silently). 0.1.2 routes both node calls
through a new Invoke-NodeCapture helper that uses Start-Process
-RedirectStandardOutput/Error to file, so PS 5.1 never touches the streams.
Multi-source TLD cascade with on-disk cache
- -Indexer now accepts a comma-separated list; default "https://navigate.st,https://silentmode.st".
- Cascade (first hit wins): indexer[s] HTTPS -> local cache
(C:\ProgramData\Ariadne\tlds-cache.json) -> electrum WSS (fetchTldList / legacy
fetchBcnrTlds) -> bundled ariadne.config.json snapshot -> bootstrap [bch].
- Authoritative fetches (indexer or electrum) refresh the cache. Second install on a
machine that has ever succeeded before is instant + always works offline.
- Per-source deadlines kept tight (5s for indexer HTTPS) so a slow source can't stall
the install.
Upgrade over live install
- .iss CurStepChanged(ssInstall) stops "BNS Resolver Daemon" + "BNS Sia Bridge" via
schtasks + 1.5s pause before file replacement. Fixes "DeleteFile failed; code 5.
Access is denied" on runtime\node.exe when upgrading a machine where the daemon is
currently running.
CA import
- Switched Import-Certificate -CertStoreLocation Cert:\... to direct X509Store('Root',
'LocalMachine').Add(). Cert: PSDrive is not always loaded in -NoProfile elevated
shells, which had broken the standalone unblock scripts.
Verified end-to-end on this box: silent install -> exit 0, clean install.log,
BNS Resolver Daemon running, 127.0.0.1:53 open, 9 NRPT rules (all TLDs including .bch),
coinspectrum.bch resolves system-wide, tlds-cache.json populated.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.2.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.2.exe
- silentmode.st + navigate.st/bns/silentmode.bch manifests advertise 0.1.2
- sha256 5a2c9e4f3c5e1a162008641019f5ffc35968751c0440f94d3c5fe67aafb8cc5e
Local Dev <noreply@localhost>
2026-09-09 22:57:06 +02:00
$store . Add ( $imported )
$store . Close ( )
2026-07-29 13:54:34 +02:00
$state . caThumbprint = $imported . Thumbprint
New-Item -Path " HKLM:\SOFTWARE\Policies\Mozilla\Firefox\Certificates " -Force | Out-Null
Set-ItemProperty -Path " HKLM:\SOFTWARE\Policies\Mozilla\Firefox\Certificates " `
-Name " ImportEnterpriseRoots " -Value 1 -Type DWord
# Also install our specific root into Firefox's own store (works even if the
# user disabled enterprise-roots). Both policies are applied at Firefox startup.
New-Item -Path " HKLM:\SOFTWARE\Policies\Mozilla\Firefox\Certificates\Install " -Force | Out-Null
Set-ItemProperty -Path " HKLM:\SOFTWARE\Policies\Mozilla\Firefox\Certificates\Install " -Name " 1 " -Value $caCert -Type String
Write-Host " [ok] BNS Local Root CA trusted (machine store + Firefox enterprise-roots & install policies) "
# Firefox only loads the enterprise-roots policy + new root at startup, so a
# running Firefox keeps rejecting .bch until it is restarted.
$ff = Get-Process firefox -ErrorAction SilentlyContinue
if ( $ff ) {
if ( $CloseFirefox ) {
$ff | Stop-Process -Force
2026-07-30 20:58:45 +02:00
Write-Host " [ok] Firefox was closed -- reopen it and .bch certificates will be trusted "
2026-07-29 13:54:34 +02:00
} else {
2026-07-30 20:58:45 +02:00
Write-Host " [!] Firefox is open -- fully QUIT and reopen it so it trusts .bch (or re-run with -CloseFirefox). " -ForegroundColor Yellow
2026-07-29 13:54:34 +02:00
}
}
} else {
2026-07-30 20:58:45 +02:00
Write-Host " [skip] no ca\root-ca.crt found -- https for .bch not enabled "
2026-07-29 13:54:34 +02:00
}
# 3b) Firefox DNS-over-HTTPS. BCNR-only names (all .bch, etc.) already work in
2026-07-30 20:58:45 +02:00
# Firefox with DoH ON -- DoH returns NXDOMAIN and Firefox falls back to the
2026-07-29 13:54:34 +02:00
# system resolver (us). Only names that ALSO exist in ICANN (a reclaimed .de)
# lose to the real record. Turning DoH off fixes those but sends Firefox's
2026-07-30 20:58:45 +02:00
# other DNS through the system resolver instead of encrypted to Cloudflare --
2026-07-29 13:54:34 +02:00
# so it's opt-in (-FirefoxNoDoH) and left unlocked so the user can re-enable.
if ( $FirefoxNoDoH ) {
New-Item -Path " HKLM:\SOFTWARE\Policies\Mozilla\Firefox\DNSOverHTTPS " -Force | Out-Null
Set-ItemProperty -Path " HKLM:\SOFTWARE\Policies\Mozilla\Firefox\DNSOverHTTPS " -Name " Enabled " -Value 0 -Type DWord
2026-07-30 20:58:45 +02:00
Write-Host " [ok] Firefox DNS-over-HTTPS disabled (opt-in) -- ICANN-conflicting names now resolve via BCNR in Firefox too "
2026-07-29 13:54:34 +02:00
} else {
Write-Host " [info] Firefox DoH left on (BCNR-only names work anyway). To also reclaim names that exist in ICANN, re-run with -FirefoxNoDoH "
}
$state | ConvertTo-Json -Depth 5 | Set-Content $stateFile
Clear-DnsClientCache
Write-Host " [done] try: https://hello.bch (uninstall: .\setup\uninstall.ps1) "
2026-09-08 01:27:51 +02:00
try { Stop-Transcript | Out-Null } catch { }