- site/releases: 0.3.77 block above 0.3.76 with the Translator engine
radio, CF Bot Fight error page and Pithos 0.3.13 bundle, SHAs
ab4135a9… (installer) + a01ad6b8… (portable).
- site/theseus and site-theseus-x: bump the download card to 0.3.77 and
the fallback hashes to match. The theseus-x page already fetches the
live manifest; the fallback stays in sync for offline reads and the
Sia mirror.
- site/releases-manifest.json: 0.3.77 entry prepended for the main
manifest; the dl copy was scp'd and dl-latest.sh regenerated the
/latest/ redirects on the VPS.
Setup 9e5c4b6b…b7e9, portable b223eb14…5c49. Pithos opens at
pithos.sia/<user>/<drive>/<folder>, music keeps playing in background
tabs, a widened left panel no longer squeezes the page; bundles Pithos
0.3.10 and Aegis 0.31.0.
A parallel ship pass raced this one on the same version string: the
other session built 0.3.75 from 4bfef3a (Aegis 0.29.0) and scp'd its
binaries over mine. The first reconcile pass accepted the on-disk
hashes under the "both built from the same HEAD" assumption, but they
are different builds — mine includes d2ddee5 (Aegis 0.30.0) and the
master tip, theirs stops at 4bfef3a. The peer stood down, I re-scp'd
my binaries, and this commit pulls the manifest + the releases page +
theseus.x back to those hashes. The release notes already named
Aegis 0.30.0, which matches what the binaries actually bundle now.
Manifest, releases page, download pages and theseus.x fallback text all point at
0.3.61 with the new hashes. Silentmode.st serves static; silentmode.bch mirror
follows via the 5-min Sia sync.
Every subsystem is now individually switchable and reports its own state.
GET http://127.0.0.1/api/status returns:
- daemon: version, startedAt, uptimeSec, policy, permittedTlds, ports,
upstream, gateway
- index: size, nameCacheSize, warmStart {source, at, size}
- sources.snapshotHttps: enabled, urls, lastAttempt/Success/Status/Error,
winnerUrl, notModified count, refreshes count, ETags
- sources.electrumWss: enabled, lastAttempt/Success/Error, failStreak, refreshes
- sources.perQueryLookup: enabled, gateway, lastAttempt, hits/misses/errors
- sources.diskCache: enabled, path, exists, size, mtime
- sources.localApi: enabled, endpoints[]
- policyFile: path, exists
policy.json schema extended with a sources.<name>.enabled block, hot-reloaded
by the existing loadPolicyOverride() on the same 5-second tick as the
collision policy. Each refresh/lookup path guards on its flag, so an operator
can isolate any subsystem without a restart:
- snapshotHttps off -> daemon skips dl.silentmode.st/Sia polling
- electrumWss off -> daemon skips chipnet WSS refresh
- perQueryLookup off -> DNS handler misses fall straight through to forward
- diskCache off -> no write to and no read from bns-name-snapshot.json
- localApi off -> /api/* return 503 with a hint pointing at policy.json
Verified locally on port 18080: /api/status returned the full JSON with a
snapshot warm-start from dl.silentmode.st recorded in warmStart, ETag captured
in sources.snapshotHttps.etags, disk cache path/size/mtime populated.
Deployed dl.silentmode.st + Sia. sha256 a16d10759efede4412f4fdb52d59424bf56bbb258bb002ab930f4a5a5aef8a41.
The matching Theseus panel UI (source table with the toggles + a Copy-status
button that fetches /api/status) belongs in the Theseus release; not touched
here to avoid colliding with settings-preload.js work in flight from a
parallel session.
Manifest and download pages point at the new build. Silentmode.st serves
static; silentmode.bch mirror follows via the 5-min Sia sync; theseus.x's
fallback text and hashes match the 0.3.60 binaries so the page reads correctly
before the client-side manifest fetch overwrites them.
- .iss pre-install hook now also kills orphan node.exe from the Ariadne
install dir (foreground diagnostic runs, crashed processes not cleaned
up by the scheduled task), path-scoped so no other app is affected.
Fixes upgrades failing with "DeleteFile failed; code 5 -- Access is
denied" on runtime/node.exe -- seen on 0.1.11 install by a user who had
run the daemon manually per an earlier diagnostic.
- bnsd.js Express gateway now serves local BNS indexer endpoints:
/api/tlds -> {source, tlds: string[], count}
/api/name/:n -> full BNS entry (same shape as navigate.st/api/name/)
/api/registry -> {source, count, names[]} full index dump
CORS wide-open (matches operator behavior). Any app on the machine can
now query BNS locally with no navigate.st round-trip. Answers off the
daemMons in-memory index which already merges snapshot warm-start +
ETag polling + per-query indexer fallback + electrum WSS.
Verified locally: hello.bch resolves via /api/name; unknown gives 404;
/api/tlds returns the CA-constrained set (bch p2p bit deviant nav test
bcnr on this boxs current CA); /api/registry returns 61 names.
Deployed: dl.silentmode.st + Sia. sha256 b041652a330fefb4561da7ca14fa84ddf3b6085fda6eb62831cb08ea56ec602d.
The daemon now uses the operator's live BNS indexer as the primary source of
truth, not chipnet electrum. Two changes to the resolver pipeline:
1) Background refresh is HTTPS-first with ETag. Every 30 s the daemon polls
dl.silentmode.st/bns-name-index.json (and s3.silentmode.st:8600 as an
independent Sia mirror) sending If-None-Match; only reparses the array
when the operator republished, does zero work otherwise. Electrum WSS
stays as a same-tick secondary that runs when HTTPS turned nothing up,
so a daemon on a network that can reach chipnet but not the operator VPS
still converges. Both broken? Keep the last-good index from warm-start.
2) Per-query indexer fallback in the DNS handler. When the in-memory index
misses, the handler calls navigate.st/api/name/<host> directly with a
5-second timeout and a 30-second LRU cache. Names registered AFTER the
last snapshot poll but BEFORE the next one now answer immediately from
the live indexer instead of falling straight through to a forward and
returning NXDOMAIN. Cache is cleared on any successful full refresh so
a de-registered name doesn't stay resolvable.
Net effect: on the class of failure that motivated this (chipnet electrum
timing out on blockchain.scripthash.get_history, or port 50004 filtered by
the network), the daemon just works. The operator's HTTPS indexer -- which
every other client in the ecosystem (Theseus mobile, Aegis, sirius.x, the
gateway itself) already reads from -- is now Ariadne's Thread's primary
too. Trust model unchanged: the snapshot always came from the operator.
Verified locally with the built binary on port 15353:
bnsd index: warm-started from https://dl.silentmode.st/bns-name-index.json (61 names)
hello.bch -> 127.0.0.1 (snapshot hit)
game.x -> 127.0.0.1 (per-query /api/name fallback -- not in .bch-heavy snapshot)
coinspectrum.x -> 127.0.0.1 (per-query fallback)
ariadne.x -> 127.0.0.1 (per-query fallback)
notregisteredxyz.x -> NXDOMAIN (indexer confirms not registered, forwarded to upstream)
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.11.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.11.exe
- silentmode.st manifest advertises 0.1.11
- sha256 58ffb06424785973d2cd656e5122061ce10b53a8dcff55605c23b5699552ee75
WSS-blocked networks
Symptom on the other box (2026-09-29): task Running, port 53 bound by our
node, `Resolve-DnsName hello.bch -Server 127.0.0.1` times out. Cause: the
daemon's initial index build was a top-level `await buildIndex()` over
electrum WSS to chipnet (port 50004). Corporate/ISP networks that filter
outbound WSS let the await hang forever; the queries fell through to
`forward()` to upstream 1.1.1.1:53, which the same firewall also blocked,
so both legs died.
0.1.10 borrows Theseus's warmFromSnapshot pattern for the system-wide
daemon. On startup, in parallel with an 8 s deadline per source:
1. HTTPS snapshot from operator VPS https://dl.silentmode.st/bns-name-index.json
2. HTTPS snapshot from Sia mirror https://s3.silentmode.st:8600/bns/name-list.json
3. On-disk cache from a previous run C:\ProgramData\Ariadne\bns-name-snapshot.json
First non-empty result wins; on any HTTPS win the raw array is written to
the disk cache so the NEXT boot is instant even fully offline. dns.bind()
runs the moment warm-start completes, whether or not electrum was
reachable. Electrum WSS refresh continues in the background on the 30 s
timer and wins over the snapshot when it succeeds because it is strictly
newer.
Snapshot format is the pre-resolved array Android's Ariadne reader already
consumes (`[{name, category, records, txid, height, updatedTxid, owner?}]`),
so the operator maintains ONE artifact for both clients. Custom URLs go in
`ariadne.config.json`.snapshotUrls.
Verified on the dev box by running the built binary manually on port 15353:
bnsd index: warm-started from https://dl.silentmode.st/bns-name-index.json (61 names)
bnsd DNS: 127.0.0.1:15353 (policy=bcnr-first, upstream 1.1.1.1)
hello.bch -> 127.0.0.1
Disk cache written: 17,674 bytes.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.10.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.10.exe
- silentmode.st manifest advertises 0.1.10
- sha256 29710c4011dfe058a252f94af221c02593e728e89cc7427e3e01eaa530f8e526
Chromium (Chrome + Edge) doesn't treat `foo.x` as a URL because `.x` isn't on
its known-TLD list -- it sends bare input to the default search engine. Only
way to intercept it without a browser extension is to make the default search
engine one that understands BNS.
The daemon now serves that. New route `app.get("/search")` before the catch-all
Express handler:
- `q` matches <label>.<permitted-tld> -> 301 https://q/ (any path preserved)
- q has whitespace, unknown TLD, IP, etc. -> 302 to `searchFallback`
(ariadne.config.json, defaults to https://duckduckgo.com/?q=)
- empty q -> 400
Permitted-TLD check reuses isPermittedHost() from lib/tlds.js -- same set the
CA is constrained to and the SNI callback gates leaf minting against, so
"routable, therefore a URL" stays one decision.
Users set their browser's default search engine to
`http://127.0.0.1/search?q=%s`. Everything decided locally, no navigate.st
round trip. Works offline. Works with the same offline BNS index the DNS side
uses. Fallback engine is per-user via config, not a locked-in default.
Verified on this box after upgrade:
"ariadne.x" -> 301 https://ariadne.x/
"game.x" -> 301 https://game.x/
"hello.bch" -> 301 https://hello.bch/
"ariadne.x/path" -> 301 https://ariadne.x/path
"Weather" -> 302 https://duckduckgo.com/?q=Weather
"bitcoin cash" -> 302 https://duckduckgo.com/?q=bitcoin%20cash
"unknown.zzz" -> 302 https://duckduckgo.com/?q=unknown.zzz
"" -> 400 bnsd search: q parameter is required
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.9.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.9.exe
- silentmode.st manifest advertises 0.1.9
- sha256 3b973e0ae584113ad776c756b16b895fd0dfb217a02bfae6376c5abb48f9a77c
Before 0.1.8, a BNS name with an on-chain `ip` record made the daemon return
the origin's public IP directly. Browsers then hit the origin's own HTTPS
server -- and if that origin's cert was not browser-trusted (which is the
whole point of the on-chain `tls` fingerprint pin next to the `ip` record),
every browser except Theseus rejected the site with ERR_CERT_AUTHORITY_INVALID
or SEC_ERROR_UNKNOWN_ISSUER. game.x and hephaestus.x both hit this. Theseus
was fine because its in-process resolver validates against the on-chain pin.
0.1.8 answers DNS with 127.0.0.1 whenever the name has BOTH `ip` and `tls`,
terminates the browser's TLS locally with our trusted leaf, and reverse-proxies
to the origin over HTTPS while validating the origin's SHA-256 fingerprint
against the on-chain pin. Same algorithm Theseus uses (main.js:pinnedHttpsGet,
cert.fingerprint256 lower-cased, colons stripped) so a site's `tls` record
means the same thing in-Theseus and system-wide.
Names with just `ip` (no pin) keep direct routing -- unchanged. Only opts in
when the owner has published a fingerprint pin, so nothing regresses for sites
that deliberately wanted direct IP routing.
Two subtle bugs found + fixed while landing this:
1. `agent: false` on the outbound https.request. With Node's default global
agent (keepAlive on), the second request onwards reuses the TLS socket
and `res.socket.getPeerCertificate(false)` comes back stripped of
fingerprint256 -- pin validation then reads "empty" and rejects with 526.
Fresh socket per request costs a handshake but is cheap next to the
pin-verify miss.
2. Removed `content-encoding` from the hop-by-hop strip list. Origin sends
gzip'd bytes; my proxy streams them unchanged. If we strip
content-encoding while forwarding compressed bytes, the browser treats the
gzip magic as text and renders garbage. content-encoding is end-to-end
per RFC 7230 -- keep it.
Verified on this box: game.x, hephaestus.x, theseus.x, sirius.x,
coinspectrum.x, hello.bch all load HTTPS 200 in strict IWR and Chromium; DNS
answers 127.0.0.1 for game.x + hephaestus.x (was their origin IP before),
still their origin IP for names without a tls pin.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.8.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.8.exe
- silentmode.st manifest advertises 0.1.8
- sha256 47450ad4555beb04fe3b90ae5f658dc2932061d011ada9a2fb507471e2f0a0cb
Feature. The daemon now hot-reloads C:\ProgramData\Ariadne\policy.json every
5 s (fs.stat mtime check, only opens the file when it actually changed) and
flips its DNS collision policy between bcnr-first and icann-first in place.
Every browser on the machine follows within seconds -- no daemon restart,
no reinstall, no UAC. The next Ariadne panel in Theseus will edit this file
directly; a text editor works too.
install.ps1 seeds the file with the current -Policy default on FIRST install
only (an upgrade never clobbers a user's later flip) and grants BUILTIN\Users
Modify on it so an unprivileged app can rewrite it. Invalid values keep the
last good policy (logged). File deleted -> daemon falls back to the CLI
policy the scheduled task was registered with.
Also fixes an intermittent "config unreadable" on daemon startup when
ariadne.config.json had a UTF-8 BOM -- same class of PS-writes-BOM bug 0.1.6
fixed in the TLD-sync path, this time in bnsd.js's own config loader.
Verified live on this box with a second daemon running on port 15353:
bnsd policy: bcnr-first -> icann-first (from C:\ProgramData\Ariadne\policy.json)
bnsd policy: icann-first -> bcnr-first (from C:\ProgramData\Ariadne\policy.json)
bnsd policy: ... has invalid policy 'cash-first' -- keeping bcnr-first
ACL check: BUILTIN\Users has Modify on policy.json, non-elevated write succeeds.
Deployed
- dl.silentmode.st/AriadneResolver-Setup-0.1.7.exe
- s3://bns/silentmode/dl/AriadneResolver-Setup-0.1.7.exe
- silentmode.st manifest advertises 0.1.7
- sha256 742f8005c2dfb47929287c009cddf7738f0d12bd56fb8f8a837df3e9b435f47f
Theseus UI wiring for this file NOT touched -- settings-preload.js has 31
lines of uncommitted work from another session; a Theseus panel selector
belongs in that session's next commit so it doesn't collide.
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
Until now only the gateway, Theseus and the desktop reader applied the
TLD co-sign rule; phones and the Windows resolver still admitted every
registration under a cosign or hidden TLD, so the network disagreed
about which names exist. Both shipped builds now carry the rule.
Android 0.19: the JSON helpers in the new TLD-map code used throwing
getters inside methods that do not declare the checked exception, which
javac rejected under the real build (the earlier compile check ran
without -bootclasspath and stopped at the aapt R errors first). Switched
to the non-throwing opt* forms. Release-signed with the existing key.
Resolver 0.1.3: no source change beyond the resolver library already
committed; the installer picks up the source tree, so this is a version
bump and a rebuild.
Manifest, releases page, tools page and theseus page point at the new
files; dl.silentmode.st/latest redirects regenerated.
Direct answers to the three complaints in one release.
"Doesn't load any results even if I type a keyword" — navigateTo used to
always append .bch to any bare label. So "bitcoin" became
https://bitcoin.bch/ and NXDOMAINed. Now: input with a space, or bare
label with no dot, routes to the user's chosen search engine. Input with
a dot (hello.bch, example.com) stays a URL. Matches Chrome/Firefox
omnibox behaviour exactly.
"Has no check for updates in settings" — new Updates section shows the
installed version + signing-cert SHA-256, with a "Check for updates now"
button that manually polls dl.silentmode.st and toasts the result. If a
newer version exists, the standard silent-prefetch → green-chip → tap-to
-install flow (unchanged from 0.15) kicks in.
"Looks poor compared to a regular browser" — Settings gains three more
sections beyond Resolution / Ariadne's Thread / Maintenance:
- Search: segmented picker for DuckDuckGo (default), Startpage, Brave
Search, Google, Kagi. Persists via SharedPreferences.
- Updates: version + cert + manual check.
- Privacy: "Clear browsing data" — cookies (CookieManager), form data,
cache, per-tab history, all in one tap. Bookmarks + tabs preserved.
- About: silentmode.st/theseus + theseus.x links, brand credit.
serveSettingsPage() signature grew to take (indexerOn, searchKey,
currentVersion) so it can render the current selection + version. Added
searchEngineSegment() helper for the picker layout.
Release: ariadne-v0.17.apk sha256
8a655081782ecc17a50a7fcd67c28625ccb0c905cf6b583bdd703a128b2b9b10
Deployed to dl.silentmode.st + silentmode.bch (Sia mirror). Site pages
bumped: silentmode.st/tools + /releases + /theseus + theseus.x.
Users on 0.15 reported "Ariadne can't open many websites" and "start page
is full of dead links". Two independent stale-data issues, both fixed.
Start page — replaces the featured-hero + beacon-fallback grid with the
same nine curated cards navigate.st serves (hello.bch, siatest.bch,
SilentMode.X, Theseus.X, Sirius.X, Hephaestus.X, Prometheus.X, Helios.X,
Hermes.X). No more auto-iteration over every registered name on the
beacon — which is what was surfacing smtest-dc5563.bch, uitest-e20579.p2p,
qrtest-e21970.nav etc. Added product-role badges (infrastructure /
navigator / registrar / code-host / app-store / search / messaging) with
the same acid/blue/purple/green/gold/pink vocabulary Theseus uses.
Bundled snapshot at app/assets/bns-index.json regenerated from the live
chain via the resolver-web.js buildIndex path. Up from 44 entries to 54,
now including all 13 .x names (was 6 — silentmode.x, theseus.x,
hephaestus.x, prometheus.x, helios.x, hermes.x etc. were entirely
missing). So cold-start resolution finds the .x brands without waiting
for a network trip.
Also regenerated dl.silentmode.st/bns-name-index.json — same 54-entry
JSON. That file hadn't been rebuilt since 2026-08-30 and is what every
client's CDN pull path reads on cold start (mobile, extension, resolver).
There's no timer that regenerates it; scratchpad/build-snapshot.mjs is
the reproducible one-shot: node build-snapshot.mjs <cdn-path>.
Sites updated in the same commit (all four surfaces need to advertise the
same version — silentmode.st/tools + /releases + /theseus + theseus.x
were still linking 0.15). APK also mirrored to bns/silentmode/ so
silentmode.bch/ariadne-v0.16.apk serves the same bytes.
Release: ariadne-v0.16.apk sha256
fa58aa7642319faaa6b23f0f0a46c0adad2ce1f3c5957f4d527ac09241b76574
Silent-background prefetch was already the model in 0.12+, but the chip
showed the same "▲ 0.14" text whether the download was running, done, or
failed. Users had no way to know they'd get "Install now" instead of
"Download & install" until they tapped.
Chip is now a small state machine driven off UpdateInstaller.Progress:
- DOWNLOADING — muted amber pill, text "▲ 0.15 · 45%" ticking live from
onProgress. Tap = "Downloading in the background, tap again when green."
- READY — green pill, "▲ Install 0.15". A brief toast fires on state entry
so the user sees the prefetch just completed. Tap = short confirmation
dialog, then straight to the platform package installer.
- RETRY — appears when the prefetch fails. One tap re-runs prefetch and
the chip returns to DOWNLOADING.
The confirmation dialog now spells out that Android replaces Ariadne in
place and reopens on the new version — matches Android's actual behaviour.
saveSession() runs before launchInstall so the newly-launched v0.15
restores tabs cleanly.
Dead code drop: promptToUpdate + downloadThenInstall (the old on-demand
progress-dialog path) — the state machine handles both silent and
tap-first flows.
Release: ariadne-v0.15.apk sha256
35532e596ec43a2da0cf49496640c72972321f5a44fa2e16d259d281a021b425
Deployed to dl.silentmode.st; site/releases-manifest.json bumped.
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>
Bookmarks (A1). ⭐ button in the address row toggles the current URL;
long-press opens the list; a Your-bookmarks section appears above Featured
on the start page. Storage in filesDir/bookmarks.json, plain JSON,
most-recent first.
Undo close tab (A2). Floating pill "Tab closed · UNDO" for 5s after any
closeTab; tap UNDO → reopen in a new tab.
Long-press link menu (A3). WebView.HitTestResult drives an Open in new tab
/ Copy link / Share link sheet; no JavaScript injection.
Downloads (A5). WebView.setDownloadListener wired to Android's system
DownloadManager. Files land in the public Downloads dir with the platform
notification. URLUtil.guessFileName picks the name.
Pull-to-refresh (A4). New PullWebView subclasses WebView with hand-rolled
overscroll detection; a gold chip fades in (pull to reload → release to
reload) as the user drags. No AndroidX SwipeRefreshLayout dependency.
Share-target intent (A6). Manifest gains SEND text/plain and VIEW
http/https filters. urlFromIntent() feeds the payload through newTab (which
inherits navigateTo's .bch shortcut and .bch.bch collapse from 0.12).
Address bar polish (B2/B3/B5). Idle display strips https:// + trailing /
(focus regains restore the full URL, select-all). Long-press copies. A
compound-drawable ic_close × appears while focused + non-empty; tap clears.
Branded splash (E1). New SplashTheme + splash_background paint the Ariadne
mark on #0B0F1A during cold start; MainActivity.onCreate swaps to
AriadneTheme before layout.
Build: d8 classes.dex step ran over Windows's 32k command-line limit; now
Push-Locations into build/classes and passes relative paths.
Release: ariadne-v0.14.apk sha256
0deace437ccde7709cd21486c9b7b37b2a57463cead79b75672d974d3e5b11b0
site/releases-manifest.json bumped. Steps in RELEASE-HANDOFF-v0.14.md.
Start page grows a Theseus-style hero: big Ariadne mark, tagline, search
input that navigates the tab. Section header separates hero from the
featured-card grid. New ariadne-search:// scheme handled in
MainActivity.handleChoice routes submissions through navigateTo (so it
inherits the .bch shortcut and .bch.bch collapse from 0.12).
Tab strip auto-hides while there's only one tab — Chrome pattern, reclaims
~44dp of top chrome for the common case. A compact tab-count button now
lives in the address row (always visible, gold when many tabs), tapping it
opens a lightweight tab switcher (title + active-mark + New tab item).
Address bar polish: 15px → 16sp text, focus outline in gold via
StateListDrawable, slightly taller padding (48dp touch target).
Bottom navbar: ⟳ reload → ⌂ home (the actually-missing action); Reload
moves into the overflow ⋮ menu as the first item.
Motion: 150ms alpha crossfade on tab switch; :active card scale on the
start page.
Release: ariadne-v0.13.apk sha256
aab1f1e1aa4654c6d2d21ee09e3067d8344697609900d6ccdbd082af95dc9ddc
site/releases-manifest.json bumped. Steps in RELEASE-HANDOFF-v0.13.md.
Fixes the compounding-suffix bug (cards linked to hello.bch.bch, then
hello.bch.bch.bch on subsequent loads): the bundled BNS index stores names
TLD-qualified (hello.bch), so the card generator no longer appends '.bch'
a second time. Featured hero lookup is now TLD-agnostic (matches by
label). navigateTo defensively collapses repeated TLD suffixes so old
bookmarks and any address-bar text with .bch.bch survive gracefully.
Start page redesigned to match TheseusNavigator/home.html: provenance
badges (on-chain / sia / server / proxy / redirect / registry), editorial
one-liner descriptions on hero cards, acid-accent .tld suffix, richer
typography.
Auto-update: on launch the app polls dl.silentmode.st/releases-manifest.json,
finds the ariadne-android entry, shows a chip when newer, silently prefetches
the APK to filesDir/updates/, verifies SHA-256 against the manifest, then
offers a one-tap Install prompt via a plain-Java UpdateFileProvider (no
AndroidX). Hash mismatch deletes the file and reports the failure — never
spawns the installer on a truncated payload (per hash-verify rule in
CLAUDE.md).
New: UpdateInstaller, UpdateFileProvider. UpdateCheck now returns filename +
sha256 + changelog. AndroidManifest: REQUEST_INSTALL_PACKAGES + provider.
Release: ariadne-v0.12.apk sha256
89c4cc28d068d2f6962a9788bf671c6f017f27fb8502f3beef8312eb7d7924f4
site/releases-manifest.json bumped. Steps in RELEASE-HANDOFF-v0.12.md.