Snapshot of the decentralized-web stack at the point of the resolver+Theseus rebuild deploy. Includes: - Argus (BNS engine + resolver daemon + Sia gateway) - AriadneResolver (Windows Inno installer bundle + Android APK sources + Firefox extension) - TheseusNavigator (Electron browser) - site/ (silentmode.st content, deployed to Sia at bns/silentmode/) - design docs, roadmap, protocol spec Secrets excluded via .gitignore: Argus/sia-s3.json, Argus/wallets.json, Argus/ca/*.key,*.crt. Build outputs, node_modules, and bundled runtimes also excluded. Shipped hashes on dl.silentmode.st at this commit: AriadneResolver-Setup-0.1.0.exe 5bcb216eef31ea28ed767e4134ab74bd5ac69dfbd365fd249e9e6938e55c986a TheseusNavigator-Setup-0.0.1.exe 7c735e88bad2da3347145adba3016c8f626a18b8422289c8c6ba471972e2952b TheseusNavigator-0.0.1-portable.exe 008fd84445babeabb401b2bca40ea9466b24b0e6d6c85104da7640c5c5c84521 ariadne-v0.2.apk 635c8f04d44ef855a8390b9eeddb8cd2d50622e81cc4e5004e8daffc1bb0425c
163 lines
9.6 KiB
PowerShell
163 lines
9.6 KiB
PowerShell
# BNS resolver daemon installer — run from an ELEVATED PowerShell.
|
|
# .\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",
|
|
# Opt-in: turn off Firefox DNS-over-HTTPS. Only needed to resolve names that
|
|
# ALSO exist in ICANN (a reclaimed .de) via BCNR in Firefox — BCNR-only names
|
|
# (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"
|
|
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
|
|
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
|
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"
|
|
|
|
# Generate the name-constrained local root CA now (on THIS machine; the key never
|
|
# ships) so the trust-store import below always finds a valid, constrained root
|
|
# instead of racing the daemon's first request.
|
|
Push-Location $root
|
|
& $node --input-type=module -e "import('./src/lib/ca.js').then(m=>m.loadOrCreateRootCA()).catch(e=>{console.error(String(e));process.exit(1)})"
|
|
Pop-Location
|
|
|
|
# 0) Idempotency: clear any previous install of ours first
|
|
Get-DnsClientNrptRule | Where-Object Comment -eq "BNS .bch resolver" | ForEach-Object {
|
|
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)
|
|
$action = New-ScheduledTaskAction -Execute $node -Argument "`"$daemon`" --upstream $Upstream" -WorkingDirectory $root
|
|
$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") {
|
|
Add-DnsClientNrptRule -Namespace ".bch" -NameServers "127.0.0.1" -Comment "BNS .bch resolver"
|
|
Write-Host "[ok] NRPT rule added: .bch -> 127.0.0.1 (all other DNS untouched)"
|
|
} 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
|
|
# original — that's exactly what left a past uninstall pointing DNS at a dead daemon.
|
|
$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"
|
|
}
|
|
$imported = Import-Certificate -FilePath $caCert -CertStoreLocation Cert:\LocalMachine\Root
|
|
$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
|
|
Write-Host "[ok] Firefox was closed — reopen it and .bch certificates will be trusted"
|
|
} else {
|
|
Write-Host "[!] Firefox is open — fully QUIT and reopen it so it trusts .bch (or re-run with -CloseFirefox)." -ForegroundColor Yellow
|
|
}
|
|
}
|
|
} else {
|
|
Write-Host "[skip] no ca\root-ca.crt found — https for .bch not enabled"
|
|
}
|
|
|
|
# 3b) Firefox DNS-over-HTTPS. BCNR-only names (all .bch, etc.) already work in
|
|
# Firefox with DoH ON — DoH returns NXDOMAIN and Firefox falls back to the
|
|
# 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
|
|
# other DNS through the system resolver instead of encrypted to Cloudflare —
|
|
# 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
|
|
Write-Host "[ok] Firefox DNS-over-HTTPS disabled (opt-in) — ICANN-conflicting names now resolve via BCNR in Firefox too"
|
|
} 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)"
|