# 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", # 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", # 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", # 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" # ---- 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' # Collision-policy override, hot-reloaded by the daemon and edited from the # Ariadne's Thread panel in Theseus. Seed with the -Policy install-time choice # ONLY on first install so an upgrade doesn't clobber a user's later flip; # grant BUILTIN\Users Modify so Theseus can rewrite it without a UAC prompt. $PolicyFile = Join-Path $LogDir 'policy.json' if (-not (Test-Path $PolicyFile)) { $seed = if ($Policy -eq 'bns-first') { 'bcnr-first' } else { $Policy } ConvertTo-Json @{ policy = $seed } | Set-Content -Path $PolicyFile -Encoding utf8 } try { $acl = Get-Acl $PolicyFile $rule = New-Object System.Security.AccessControl.FileSystemAccessRule( [System.Security.Principal.SecurityIdentifier]::new('S-1-5-32-545'), # BUILTIN\Users 'Modify','Allow') $acl.SetAccessRule($rule) Set-Acl -Path $PolicyFile -AclObject $acl } catch {} 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)`nScriptStack:`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 } if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { try { Stop-Transcript | Out-Null } catch {} 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" # 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 {} # --- helper: run `node --input-type=module -e "