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' }
Ariadne 0.1.5: route and certify every live registry TLD, not the legacy nine
Other browsers stopped opening most BNS names because two lists had
drifted. The installer took its TLD set from the gateway's `advertised`
field — the legacy tlds.bch record, nine TLDs — while the registry had
moved to per-TLD NFTs with twenty (including .x, where most Silent Mode
sites live). And the daemon's permitted-TLD list came from the bundled
ariadne.config.json (seven TLDs) rather than the CA the installer had
just minted, so only the overlap — .bch, .p2p, .deviant, .nav — actually
worked; .sia, .nt and the rest got no certificate, .x got no route at
all.
Now: the gateway publishes `routable` (every live TLD on the public
listing plus the legacy advertised set); the installer routes and
certifies that, skipping ICANN-delegated collision TLDs (.cash, .gt,
.sc — checked against the live IANA root zone, bundled snapshot as
fallback) since the local CA must never be constrained to a real
internet TLD; it writes the certified list into ariadne.config.json;
and the daemon reads its permitted list from the installed CA's own
name constraints, so the two can no longer disagree.
2026-09-27 23:20:26 +02:00
# Prefer the registry's routable list (every live per-TLD NFT plus the
# legacy advertised set); fall back to assembling it from tlds[] + advertised
# for a gateway that predates the field; last resort: advertised alone (the
# legacy tlds.bch record — nine TLDs, which is how 0.1.4 missed .x).
if ( $r -and $r . routable -and $r . routable . Count -gt 0 ) {
return @ { tlds = @ ( $r . routable | ForEach-Object { [ string ] $_ } ) ; source = " indexer( $IndexerUrl ) " }
}
if ( $r -and $r . tlds -and $r . tlds . Count -gt 0 ) {
$live = @ ( $r . tlds | Where-Object { -not $_ . hidden } | ForEach-Object { [ string ] $_ . tld } )
$adv = @ ( ) ; if ( $r . advertised ) { $adv = @ ( $r . advertised | ForEach-Object { [ string ] $_ } ) }
$u = @ ( $live + $adv | Where-Object { $_ } | Select-Object -Unique )
if ( $u . Count -gt 0 ) { return @ { tlds = $u ; source = " indexer( $IndexerUrl ) " } }
}
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
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 }
}
Ariadne 0.1.5: route and certify every live registry TLD, not the legacy nine
Other browsers stopped opening most BNS names because two lists had
drifted. The installer took its TLD set from the gateway's `advertised`
field — the legacy tlds.bch record, nine TLDs — while the registry had
moved to per-TLD NFTs with twenty (including .x, where most Silent Mode
sites live). And the daemon's permitted-TLD list came from the bundled
ariadne.config.json (seven TLDs) rather than the CA the installer had
just minted, so only the overlap — .bch, .p2p, .deviant, .nav — actually
worked; .sia, .nt and the rest got no certificate, .x got no route at
all.
Now: the gateway publishes `routable` (every live TLD on the public
listing plus the legacy advertised set); the installer routes and
certifies that, skipping ICANN-delegated collision TLDs (.cash, .gt,
.sc — checked against the live IANA root zone, bundled snapshot as
fallback) since the local CA must never be constrained to a real
internet TLD; it writes the certified list into ariadne.config.json;
and the daemon reads its permitted list from the installed CA's own
name constraints, so the two can no longer disagree.
2026-09-27 23:20:26 +02:00
# Split the activated set: TLDs that ICANN has delegated (.cash, .gt, .sc are
# registered in BNS on purpose as collision tests) are neither routed to the
# daemon nor certified by the local CA — the daemon's upstream forwarding has
# an open EDNS/AAAA concern, and a CA constrained to a real internet TLD could
# vouch for real sites. The IANA root-zone list is fetched live (8 s) and falls
# back to the snapshot bundled next to this script. "bcnr" is always certified:
# the daemon's default certificate is issued for gateway.bcnr.
$ianaList = @ ( )
try {
$ianaText = ( Invoke-WebRequest -Uri 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt' -TimeoutSec 8 -UseBasicParsing ) . Content
$ianaList = @ ( $ianaText -split " `n " | ForEach-Object { $_ . Trim ( ) . ToLower ( ) } | Where-Object { $_ -and -not $_ . StartsWith ( '#' ) } )
Write-Host " [ok] IANA root zone: $( $ianaList . Count ) delegated TLDs (live) "
} catch {
$snap = Join-Path $PSScriptRoot 'iana-tlds.txt'
if ( Test-Path $snap ) { $ianaList = @ ( Get-Content $snap | ForEach-Object { $_ . Trim ( ) . ToLower ( ) } | Where-Object { $_ } ) ; Write-Host " [!] IANA list from bundled snapshot ( $( $ianaList . Count ) TLDs) " -ForegroundColor Yellow }
else { Write-Host " [!] no IANA list available -- routing every activated TLD " -ForegroundColor Yellow }
}
$ianaSet = @ { } ; foreach ( $t in $ianaList ) { $ianaSet [ $t ] = $true }
$COLLISION_TLDS = @ ( $ACTIVATED_TLDS | Where-Object { $ianaSet . ContainsKey ( $_ . ToLower ( ) ) } )
$ROUTE_TLDS = @ ( $ACTIVATED_TLDS | Where-Object { -not $ianaSet . ContainsKey ( $_ . ToLower ( ) ) } )
if ( $ROUTE_TLDS . Count -eq 0 ) { $ROUTE_TLDS = @ ( 'bch' ) }
$CA_TLDS = @ ( $ROUTE_TLDS + 'bcnr' | Select-Object -Unique )
if ( $COLLISION_TLDS . Count -gt 0 ) { Write-Host " [info] left to ICANN (delegated, resolved by the upstream): $( $COLLISION_TLDS -join ', ' ) " }
Write-Host " [ok] routing + certifying: $( $ROUTE_TLDS -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
# 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 "
Ariadne 0.1.5: route and certify every live registry TLD, not the legacy nine
Other browsers stopped opening most BNS names because two lists had
drifted. The installer took its TLD set from the gateway's `advertised`
field — the legacy tlds.bch record, nine TLDs — while the registry had
moved to per-TLD NFTs with twenty (including .x, where most Silent Mode
sites live). And the daemon's permitted-TLD list came from the bundled
ariadne.config.json (seven TLDs) rather than the CA the installer had
just minted, so only the overlap — .bch, .p2p, .deviant, .nav — actually
worked; .sia, .nt and the rest got no certificate, .x got no route at
all.
Now: the gateway publishes `routable` (every live TLD on the public
listing plus the legacy advertised set); the installer routes and
certifies that, skipping ICANN-delegated collision TLDs (.cash, .gt,
.sc — checked against the live IANA root zone, bundled snapshot as
fallback) since the local CA must never be constrained to a real
internet TLD; it writes the certified list into ariadne.config.json;
and the daemon reads its permitted list from the installed CA's own
name constraints, so the two can no longer disagree.
2026-09-27 23:20:26 +02:00
$tldsArg = ( $CA_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
Ariadne 0.1.5: route and certify every live registry TLD, not the legacy nine
Other browsers stopped opening most BNS names because two lists had
drifted. The installer took its TLD set from the gateway's `advertised`
field — the legacy tlds.bch record, nine TLDs — while the registry had
moved to per-TLD NFTs with twenty (including .x, where most Silent Mode
sites live). And the daemon's permitted-TLD list came from the bundled
ariadne.config.json (seven TLDs) rather than the CA the installer had
just minted, so only the overlap — .bch, .p2p, .deviant, .nav — actually
worked; .sia, .nt and the rest got no certificate, .x got no route at
all.
Now: the gateway publishes `routable` (every live TLD on the public
listing plus the legacy advertised set); the installer routes and
certifies that, skipping ICANN-delegated collision TLDs (.cash, .gt,
.sc — checked against the live IANA root zone, bundled snapshot as
fallback) since the local CA must never be constrained to a real
internet TLD; it writes the certified list into ariadne.config.json;
and the daemon reads its permitted list from the installed CA's own
name constraints, so the two can no longer disagree.
2026-09-27 23:20:26 +02:00
# The daemon reads its permitted-TLD list from ariadne.config.json (and, since
# 0.1.5, from the CA's own constraints); write the same list the CA was minted
# with so the two can never drift apart again.
$cfgPath = Join-Path $root 'ariadne.config.json'
try {
$cfg = Get-Content $cfgPath -Raw | ConvertFrom-Json
$cfg . tlds = @ ( $CA_TLDS )
$cfg | ConvertTo-Json -Depth 10 | Set-Content -Path $cfgPath -Encoding utf8
Write-Host " [ok] ariadne.config.json tlds = $( $CA_TLDS -join ', ' ) "
} catch { Write-Host " [!] could not update ariadne.config.json tlds: $( $_ . Exception . Message ) " -ForegroundColor Yellow }
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.
Ariadne 0.1.5: route and certify every live registry TLD, not the legacy nine
Other browsers stopped opening most BNS names because two lists had
drifted. The installer took its TLD set from the gateway's `advertised`
field — the legacy tlds.bch record, nine TLDs — while the registry had
moved to per-TLD NFTs with twenty (including .x, where most Silent Mode
sites live). And the daemon's permitted-TLD list came from the bundled
ariadne.config.json (seven TLDs) rather than the CA the installer had
just minted, so only the overlap — .bch, .p2p, .deviant, .nav — actually
worked; .sia, .nt and the rest got no certificate, .x got no route at
all.
Now: the gateway publishes `routable` (every live TLD on the public
listing plus the legacy advertised set); the installer routes and
certifies that, skipping ICANN-delegated collision TLDs (.cash, .gt,
.sc — checked against the live IANA root zone, bundled snapshot as
fallback) since the local CA must never be constrained to a real
internet TLD; it writes the certified list into ariadne.config.json;
and the daemon reads its permitted list from the installed CA's own
name constraints, so the two can no longer disagree.
2026-09-27 23:20:26 +02:00
foreach ( $tld in $ROUTE_TLDS ) {
2026-08-01 02:06:48 +02:00
$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 { }