Setup 0fd8c7c3b93d906a85fc1adfce9d1910b1f40e1062cd81e9ba3043236b29a4a4
Portable 84f6c67d767dc82dc61ccab78909741bd30cd2cfa0d431f30fceef7dcb779679
One bundled fix since 0.3.24:
cbdc755 - Settings > Performance / Privacy / Extensions sidebar links
were dead because showSection()'s sections array still listed 'naming'
(deleted when Registries folded into General in 0.3.21). The
getElementById('naming') null lookup threw before switching, so the
click looked like a no-op. Dropped 'naming' from the array.
Deployed. Verified LIVE 0.3.25.
Setup f24d16a43021cea1b7317ae5e0aa24fe7c9b1264332dfba92b82d1a2057deb67
Portable d5e64ab5e9e038b658692eb9c615f0189c2547f91d96d50044fb0f7dbc39a0d3
Bundled since 0.3.23:
a730b56 - Ariadne 0.1.1 fetched from silentmode.st on demand instead of
bundled with Theseus. Installer size drops ~24 MB; Ariadne can update
independently.
3e780eb - Light-mode fix: .btn text (Check for updates, Add engine
button) and .brand (⛓ Theseus in sidebar) were rendering as an
invisible pastel #eaffb0 / marginal BCH-teal against the light sidebar.
Route through var(--acid) with a light-media override for .brand.
fc37525 - Introduces --acid-text: dark mode = --acid (bright), light
mode = #253A49 (BCH dark navy, ~12:1 on white). Every text role in
settings.html now uses --acid-text — .brand, .side a.active, .btn,
.engcat hover, inline <b style> in the Ariadne + Update status
renders. Fills / borders / tints stay --acid for BCH-green identity.
Deployed. Verified LIVE 0.3.24.
Setup 9dfd5a6ceb36b7d34216cfae3bff58c612c641cefbc9cd784182fdb1797d0e95
Portable 222628d481d4f46b2ebfd0bbd104cbafb27c125bfc7fc64cb216c04f871c72d4
Two bundled changes since 0.3.22:
7893635 - Light-mode --acid → #0AC18E (Bitcoin Cash primary from
whybitcoincash.com's palette). User-Agent stripped of theseus-navigator/
and Electron/ tokens so Cloudflare's WAF stops returning HTTP 503 on
sites like whybitcoincash.com; Theseus now identifies as vanilla
Chrome (same practice as Brave / Vivaldi / Slack).
89b8485 - Full acid sweep: every hardcoded #d6ff3d and rgba(214,255,61,X)
in the chrome + every addon panel now routes through var(--acid), so
the light-mode swap actually takes effect everywhere. Theseus button
(.logo) + update chip (.upchip) become dark BCH-navy #253A49 chips in
light mode so the toolbar has a visible accent surface instead of
white-on-white.
Deployed. Verified LIVE 0.3.23.
Setup fc779af12d55224e3d5eafdde3feabfc616b56b2737f953e7ecb32ef90461cea
Portable 046526f2e73c2026d3243ca9e4639374631cc66924b247feb92f224c9520af4b
Two bundled changes since 0.3.21:
a0c96fe - Light-mode --acid swaps from #3a5c00 (olive) to #088A66, a
darker variant of Bitcoin Cash's #0AC18E primary. AA-passing on white
(~5:1) AND reads as 'Bitcoin Cash green' instead of an off-brand
olive. Applied across chrome / settings / error / home / approval /
messages / bchwallet / siawallet / screenshot editor. Dark mode's
#d6ff3d is unchanged.
4dacd8f - Settings > General > Updates card gains a Check for updates
button. Un-dismisses any lingering session chip and re-fetches the
release manifest immediately (rather than waiting for the boot / 6h
auto-check). Renders 'You're on the latest (v0.3.22)' or 'vX.Y.Z is
available — the update chip will offer it'.
Deployed. Verified LIVE 0.3.22.
Setup a2e25a80ad2300e79cf5e7de1337bbc986b48e6bda9c13f497caf5b66c90edb9
Portable 173d376aa23e6cb28f8a353206a15144f8a04d9817f531902b4ba93039101742
One bundled change since 0.3.20:
509b32f - Settings > General drops its Toolbar dropdowns (drag handles
have covered that job since 0.3.15). Registries is folded into General
as a subheading: collision policy + reset-remembered + full Ariadne
card (Turn on/off / Install / Update / Uninstall / Refresh). Sidebar
entry 'Registries' removed. Underlying settings + IPC unchanged.
Deployed. Verified LIVE 0.3.21.
Setup 8bc234a69534b7259b059cafab8c93615d41fc804fcdff1144b691c3b3ac9477
Portable e3b0ab8ed62f6172288a9511ff1ecb4e50d7ef9effb06ed4b728e2e7d467a55b
One bundled change since 0.3.19:
763c68d - Home page search box no longer hardcodes 'DuckDuckGo' in the
placeholder. New window.home.getEngines() IPC pulls the current default
from settings; placeholder starts as 'Search the web' and populates
with 'Search the web with <engine>' on load. Fallback URL (never fires
in normal use) swapped from duckduckgo.com to startpage.com to match
Theseus's own default engine.
Deployed. Verified LIVE 0.3.20.
Setup 4830da019914019c5d3575420b490a278bc3ba63606680f84c30d58de5ff9500
Portable 1ef76b5b0fae9ac5a201956f6f3da55b1f1ceec8a035cc0d498147a210491b5f
One theseus change since 0.3.18:
7672ce0 - Signed add-on update endpoint activates. The Silent Mode
operator Ed25519 pubkey (generated 2026-09-07,
732b1263a236b0030383a2376597cfa43c3624b3ca2912a46134f8f2a06e6012)
is baked into addon-update-pubkeys.js, so on every boot Theseus polls
each installed add-on's updateURL 30 s in, verifies the signed
updates.json against the pubkey, and stages any newer signed version
under <userData>/addons-updates-staged/ for promotion on the next
launch. Verification, backup, and promotion mechanics unchanged from
0.3.18. The screenshot add-on already advertises
https://addons.silentmode.st/screenshot/updates.json; publishing a
signed entry there is what activates real updates. No entry is
published yet, so this build's boot-time fetch fails silently until
the operator lands the first signed payload via
scripts/sign-addon-update.mjs.
3be152a - Also in this ship: package-lock.json resynced with
package.json. The 0.3.18 ship inadvertently committed WIP dependency
additions (bitcoinjs-lib, bip32, bip39, ecpair,
@bitcoinerlab/secp256k1) via git commit -o's file-scoped semantics
without a matching lock update. Fresh clones now build cleanly with
npm ci. Deps are unused by shipped code at this time but ride along
in node_modules — installer size grew ~400 KB.
Deployed. Verified LIVE 0.3.19.
Setup b751b5deea997bb7e0e894104dcb7411358728987196cb610f316edacaf9be54
Portable 8a258f29c62a03b745e93ea0265285e6d5df38d4f25cdbdadc20b4d2a54b97d7
Five theseus fixes since 0.3.16:
e90062a - Bundled add-on updates now actually land. seedBundledAddons()
previously copied a bundled add-on only when its target folder was
missing, so the 0.3.14 screenshot editor never reached machines that
already had an older screenshot/ folder from a previous run — Theseus
quietly kept using the stale copy. The seeder now compares bundled and
on-disk addon.json versions and reseeds with a timestamped backup
under <userData>/addons-backups/<id>-<oldver>-<timestamp>/.
ecfd481 + 6117429 - Signed add-on update endpoint, à la Firefox XPI.
An add-on can now advertise an updateURL in its addon.json and be
republished at any time without waiting for a Theseus release. The
client fetches, verifies an Ed25519 signature over
"silentmode.addon-update-v1|<id>|<version>|<tarball-sha256>",
downloads the tarball, verifies the hash, and stages the new copy
under <userData>/addons-updates-staged/ for promotion on next launch.
Dormant in this build — the shipped addon-update-pubkeys.js is empty,
so checkAndStageUpdates() short-circuits and makes no outbound
requests; the feature activates when an operator ceremonies a key in
and ships a follow-up release with the pubkey baked in. Operator
tooling in scripts/generate-update-keypair.mjs and
scripts/sign-addon-update.mjs; full brief in docs/ADDON-UPDATES.md.
End-to-end verified against a local HTTP server: sign, serve, fetch,
verify, download, extract, stage, promote, backup — plus signature
tamper, wrong pubkey, sha256 tamper, and empty-pubkey short-circuit
all rejected as expected. 15/15 checks pass.
bfe5132 - Tab-switch flash is gone. Two independent causes: (a) tab
views were created without an explicit background color, so the first
frame after setVisible(true) showed whatever was underneath the view
until the page painted; a solid theme-tracking ground fills the gap.
(b) setActive iterated tabs in list order, so if the outgoing tab
came before the incoming in the array, the loop hid the outgoing
first and left one frame where no tab was visible; the incoming is
now shown before any hides.
a5a667d - Screenshot toolbar-menu captures no longer come out blank.
The click handler dispatched capture synchronously while the native
Menu.popup window was still on top, marking the tab view occluded and
letting WebContents.capturePage() snapshot a stale/empty compositor
frame at the correct dimensions (which the existing 0x0 retry
couldn't detect). Dispatch now runs from the popup's close callback
after a 120ms settle so the parent window is foreground and the
compositor is live at capture time.
a5a667d also - Ariadne — Install / Update / Uninstall alongside Turn
on / off. The Ariadne toggle card in Settings > Registries grows
three lifecycle actions. Install and Update run the bundled
AriadneResolver-Setup-<ver>.exe silently and elevated (/VERYSILENT
/SUPPRESSMSGBOXES /NORESTART — one UAC prompt, no wizard); Update is
only visible when the bundled version is newer than what's installed.
Uninstall reads Inno's QuietUninstallString from HKLM registry and
runs it elevated. Status surfaces installed version + bundled version
so the user can see what's on disk vs what would land next; buttons
disable during work and refresh after both success and failure so the
UI never lies.
Deployed. Verified LIVE 0.3.18.
Decouples bundled-add-on updates from Theseus releases. An add-on
whose addon.json declares an updateURL can be republished at any time
without shipping a new Theseus installer; existing installs pick it up
on the next boot's +30 s background check.
Client flow (main-process only, no UI touchpoints in this commit):
initAddons()
├── promoteStagedUpdates() # promote signed stage if newer
├── seedBundledAddons() # bundle wins over on-disk if newer
└── AddonHost.discoverAndActivate()
30 s later:
└── checkAndStageUpdates() # fetch, verify, download, stage
Signature: Ed25519 over
"silentmode.addon-update-v1|<id>|<version>|<tarball-sha256>",
verified against a hardcoded set of operator pubkeys living in
addon-update-pubkeys.js. Domain-separated so the operator key can't
be tricked into signing a message with a different purpose. Empty
pubkey array is the shipping default — checkAndStageUpdates() then
short-circuits and no outbound requests are made, which is the safe
posture until the operator ceremonies a key in.
Payload: gzipped tar, extracted with the system tar (present on
Win10 1803+, macOS, Linux). Path traversal defended by tar's default
refusal of `..` entries; the extracted manifest's id + version are
re-checked against the signed values before staging.
Staged updates go to <userData>/addons-updates-staged/<id>-<version>/.
Promotion into <userData>/addons/<id>/ reuses seedBundledAddons's
backup dance: existing folder moves to
<userData>/addons-backups/<id>-<oldver>-<timestamp>/ so any local
edits survive.
New files:
- addon-updater.js — client
- addon-update-pubkeys.js — hardcoded pubkeys (empty; edit + rebuild to rotate)
- scripts/generate-update-keypair.mjs — one-time keygen
- scripts/sign-addon-update.mjs — operator packager+signer
- docs/ADDON-UPDATES.md — operator brief + threat model
Wired into main.js at boot; screenshot add-on's addon.json advertises
the reference updateURL for when the endpoint goes live.
Setup f9d0654572e110f6895951dc22ec9fb4549b3a8fb14714770579264b62051259
Portable 7ff7a1501bc7d7407ec0a4974aa26652d6d7ecabcc796584254a8bb463d43b09
Two bundled fixes since 0.3.15:
4f498a1 - Extension dock no longer stacks into a column when dragged
narrow. #extbuttons + .extdock get flex-wrap:nowrap + overflow:hidden;
a second data-extcollapse signal fires when .bar's contents overflow
(alongside the width-based level 3), collapsing the row into the
single 🛡 puzzle button. Hysteresis (cached natural width + 8px slack)
keeps the ResizeObserver from oscillating across the boundary.
e160dac - toolbar-menu popup goes native (Menu.popup from main) so it
escapes the chrome-view height clipping. capturePage retries transient
0x0 results up to 6 times so the screenshot addon doesn't silently
produce a blank PNG right after a navigation.
Deployed. Verified LIVE 0.3.16.
Setup 81b6a8d2a64d20641c4cc448d35627fb3c492ea45adb24fa5c41a9f63663dd2d
Portable 47a22d86a08305536b4d80f4072ad38179a4935f303ac31bcd05dba36d5f4d40
One bundled commit since 0.3.14:
6dae15e - Drag handles on the trailing edge of the URL bar + leading
edge of the search box let the user resize live; widths persist as
urlBarWidthPx / searchBoxWidthPx and override the discrete size preset.
Also fixes the sliding bug the user just flagged: capping the URL bar
via Settings > Appearance was letting the download / extension /
Theseus buttons pack next to the URL bar, sliding leftward. Adding
margin-right: auto to the capped .urlwrap absorbs the slack so the
right group stays pinned to the right edge. Verified via CDP: logo's
rightGap stays at 10px across default / compact / urlBarWidthPx=500 /
reset.
Deployed. Verified LIVE 0.3.15.
Setup 238f0b81afd93d7cef80b9403edef1dc3e1d4c1bf29e29a498115e1185ce1db8
Portable d8877e696ad6425aa6d56cb796048702cfb37b40ff539a8c1af0d08d0a31870c
Three bundled fixes since 0.3.13:
- (Aegis multi-chain wallet, cherry-picked from claude/sleepy-maxwell-251ee6
worktree) — bchwallet add-on turns into a chain-agnostic wallet
manager with BCH + Tron mainnet + Tron Nile testnet; addon id stays
"bchwallet" so vault-derive paths + legacy BCH funds are untouched.
Dapp bridge on any https page exposes window.tronWeb / window.tronLink
matching TronLink chain ids so Tron dapps just work.
- 3d3dfe5 — Screenshot addon reworked: dock icon opens a small
toolbar-menu (Visible / Full page / Region…) instead of a sidebar
panel; captures open in a full browser tab with an editor
(crop / arrow / rect / circle / freehand / text / blur / undo / save /
copy). Two new addon-host capabilities land: toolbar-menu + open-tab.
- dd31b88 — Light-mode --acid went from #4d7300 → #3a5c00 (~7:1 on
white), and the missed addon panels (bchwallet, siawallet,
screenshot editor.css) got their overrides so nothing resolves to
the bright #d6ff3d anymore.
Note on Nile testing: the code paths verified via a running instance —
dock shows 🛡 Wallet, chain picker "🟨 BCH — main ▾" is present. Live
Nile send requires a vault + testnet TRX from nileex.io/join/getJoinPage.
Deployed. Verified LIVE 0.3.14.
Setup 7d88e4c46b02448e40d6075d10f2c6688c6d60c5a9ec41b9cbcb7684f131d6e1
Portable 5a4bcc6abb21c23729d79dd600142df4f171cc3d6bf71716dae1c802ac10e48f
Bundled since 0.3.12:
1514793 - Settings > Registries gets an on/off toggle for Ariadne's
Thread (system-wide BCDN resolver for non-Theseus browsers). Query is
silent Get-ScheduledTask; toggle spawns elevated PowerShell (UAC once
per action). Three states: running / stopped / not-installed.
b16f0a1 - New BCDN 'p' record type in Argus record-picker + Theseus
serving. Reverse-proxies an upstream URL under a BCDN name, keeping
the BCDN name in the address bar; uses upstream's own DNS + public
CA + Host header (unlike 'ip' which pins IP + on-chain TLS fingerprint).
Placed after 'ip' in the apex chain, suppressed under subdomain
inheritance so a 'p' name doesn't silently proxy every subdomain.
cfec253 - Argus registrar gains buildTldRegistrationTx + TLD_BEACON +
normalizeTld exports for minting per-TLD certificates per the TLD-
registry design.
bbfc05c - Bundled Screenshot add-on: capture-tab capability + sidebar
launcher for visible / full page / region modes; saves to Downloads.
Follow-up task_b9608dc6 will rework this into a full-tab editor.
Deployed. Verified LIVE 0.3.13.
Setup a28b4d2be705069f35fa21c5c3e008627661b6173d1eb3014d001eb81c72761e
Portable 5dab9a17818d263706704ab867b6ab9743c2606eb60e2391d0e00a78604cd26b
Two bundled fixes since 0.3.11:
e7dfa46 - At responsive level 3 the collapsed extension button was a
generic 🧩 puzzle piece. Swap it for the first registered extension's
own icon (📝 / ₿ / Ⓢ / whatever ships first). Puzzle piece stays as the
empty-state fallback when no addons are registered. Click still opens
the same dropdown of all installed extensions.
95cc2c7 - Remove the redundant 32-px extension picker strip that
sidebar-preload.js was injecting at the top of every add-on panel.
The toolbar extension dock is the canonical switcher now; doubling
that inside the sidebar just wasted vertical space and made narrow
panels cramped. Every panel reclaims its top 33px.
Deployed. Verified LIVE 0.3.12.
Setup b08530a69d47c2525a2238bd35542d141c4cf2be09f61f7b6d9bb08166f606cf
Portable b67387c85f6d37df2d2c2055a1773954a2e9019d3d567943d8ed5a39e9ee53a9
One bundled fix since 0.3.10:
beca0fa - A ResizeObserver on .bar sets data-responsive to one of four
levels based on width. CSS reacts: search box auto-hides at level 1,
Theseus button collapses to just the gear icon at level 2, and the
extension dock folds into a single 🧩 puzzle button opening a dropdown
of installed extensions at level 3. User's explicit Address bar /
Search box size settings still win at wide widths.
Deployed. Verified LIVE 0.3.11.
Setup d9801fe6a1ea7d54132db36b2c78311cd5c77912618687fb9cf6476ce75a97f9
Portable ff6ea386752fb3ceeb9d5f385258a6f01f7266c2509f4670419ce6e44e24d442
One bundled fix since 0.3.9:
d1f3347 - The brand acid green (#d6ff3d) was ~1.3:1 contrast on
#ffffff / #f6f8fb, so it went nearly invisible any time the user
flipped Settings > Theme to Light. Every chrome page (chrome, home,
settings, error, approval, messages) now overrides --acid to #4d7300
in its prefers-color-scheme: light block — same hue family, ~5.5:1
on white. Also added the missing --acid: #d6ff3d declaration to
chrome.html's :root (was relying on var(--acid, #d6ff3d) fallbacks,
so the light override couldn't bind). Verified live via CDP.
Deployed: scp + sia-upload of both trees. Verified LIVE 0.3.10.
Two background tasks in progress (screenshot extension, Aegis
multiwallet + Tron/Nile) will land in a later release.
Setup cc676e62057b42c1e1221f0e9b69a55806bab90259bbbb4e6eb64701ab3d420a
Portable 1ab811f7edba74220b452763ee0f3329ba91d84c834d3e38be6426de0b04f1c7
Two changes since 0.3.8:
f1d117e - .extbtn was display:grid, which is block-level, so multiple
extension buttons inside #extbuttons stacked vertically. Switch to
inline-grid — same icon-centering, no forced line break between
siblings. Verified on a running instance: all 4 dock buttons at the
same y-coordinate.
(this commit) - Remove the static Aegis ₿ placeholder button and its
handler. Redundant now that the real bchwallet addon ships in the box
and registers its own dock entry. Also flip the dock-hidden logic to
disappear entirely on a fresh install with no add-ons registered, so
the toolbar doesn't carry an empty slot for users who never install
one.
Deployed: scp + sia-upload of both trees. Verified LIVE 0.3.9.
Setup b2b7819e7a5f1ae1aa7ac79dd8e150f7c11f238eb3ab98a53fae51186dd4dc5b
Portable c971931f380245e44064720b132859917039488c12e0f654a2eeb406cf465d37
Bundled fixes since 0.3.7:
b649398 - User-resizable address bar + search box. Settings > Appearance >
Toolbar now offers urlBarSize (wide/medium/compact) and searchBoxSize
(hidden/compact/normal/wide). Applied as data-attrs on .bar so the flex
basis of .urlwrap and the width of .searchbox swap live. Motivation:
the per-extension dock (0.3.7) needs room to grow as users install more.
Settings-set broadcasts settings-update to chrome so resizing is instant
without a relaunch.
ab87576 - F12 / Ctrl+Shift+I opens Chromium DevTools on the active tab
in a detached window. Wired in the same before-input-event handler that
owns reload / sidebar shortcuts. Always targets the active tab
regardless of which view received the keystroke.
Deployed: scp + sia-upload of both trees. Verified LIVE 0.3.8.
VPS archive step 7: 0.3.6 pair moved to Sia dl-archive/, only 0.3.7 +
0.3.8 remain on /opt/silent-mode/dl/ (per shipping-mirrors memory).
Setup 37fd8db5388cc2486b48d282bf38e1d5b18a40de5b7319950edcc412f3ddb683
Portable befab4c21ef78d7c92eb7d399e163d3e53e202a0bda9fe95f8d8db916db2e242
Bundled fixes since 0.3.6:
1026b08 - Per-extension toolbar dock: replaces the single sidebarbtn with
one button per registered addon sidebar-panel. Notepad (📝) and Silent
Mode Relay (🌐) appear automatically from their manifest icons; a static
Aegis Wallet placeholder (₿) marks the upcoming built-in BCH wallet.
Click a live button → open the sidebar on that panel, click active →
collapse. Preload adds openSidebar(panelId) / closeSidebar() wrappers.
622aaee - Right-click "Search for '<selection>'" in the page context
menu. Uses SEARCH() so it honours the current default engine, opens in
a new foreground tab, and truncates the label at 40 chars.
Deployed: scp + sia-upload of both trees. Verified LIVE 0.3.7.
Ops note: silentmode.st /var/log had grown to 6GB (journal + rotated
syslog), which broke the first portable upload. Vacuumed journal to
500M cap; ~5.9G free after cleanup. Old Theseus builds not touched.
Three opt-in capabilities for add-ons, plus the plumbing they need:
- vault-derive: api.vault.derive("<id>/<path>") resolves once the password
vault is unlocked with a 32-byte HKDF child of the vault root under
"silentmode/addons/<path>". Path must start with the add-on id.
- page-inject: manifest "page-inject" {preload, origins}; a session-wide
preload asks main (sync, against the committed URL) which add-on bridges
apply and runs them in the isolated world with a scoped `theseus` object.
- approval-modal: api.approvalModal({title, body, origin, rows, actions,
checkbox}) shows a consent overlay over the tab area (approval.html);
resolves to the picked action id, "cancel", or "<id>+<checkbox>".
- api.onMessage/emit + window.silentmode.invoke/on for panel <-> activate()
messaging; page bridges use addon-page-msg, gated by tab + origin match.
- api.require so add-ons can share Theseus's dependency tree.
Setup e2a30a3b280448f8eb6e08efd21e208022b2a37cef14a72076b3f32b7a2fc529
Portable eca7cf3347a958307b8e2ba4f6266c81f8ec691b0c98ed6e24fdd70e65723b1e
Bundled fixes since 0.3.5:
d3c3298 - Chrome-view gap root cause: body no longer inherits the chrome
view's height, so document.body.scrollHeight now measures actual content
and syncHeight can shrink chrome back after a menu close. The growing
blank strip above the page is gone (was previously ratcheting to the
260px clamp on every menu open).
1b4a3c3 - Bookmarks: favicons on every row (backfilled on revisit for
older entries) + Edit title actually works via an in-page modal (Electron
BrowserViews disable window.prompt, which is why the previous menu item
silently did nothing).
952bf5f - Tab tooltip on hover shows title + URL, the way Chrome/Firefox
do it. Uses the url field already present in the per-tab payload.
f591475 - Error page gets a first-class "You appear to be offline" kind
that fires when navigator.onLine is false — no more false "not registered"
verdicts when you're just disconnected. Plus a "did you mean" row that
suggests near-match BCNR names (Levenshtein ≤ 2, same TLD) from the warm
shared index — no network required, works offline. Click a suggestion
to retry with the original path preserved.
b9d70e4 - 750ms self-heal on chrome-view height as belt-and-suspenders
for any future overlay path that bypasses closeAllMenus.
Deployed: scp + sia-upload of both trees. Verified LIVE 0.3.6.
Setup 935d637ae19ea821f7e89b0f9a802b4e774b6d1ae3254a70f3e5f17f89424177
Portable bb6bea253d6f32f86e8fdd152cf4c969cf311c991329d5e94fce31215ecf7f00
Chrome-view height gap: any tabs event that fires while no menu/popover
is open now normalises the chrome-view height via syncHeight(). A leaked
menu-close path could previously leave the strip inflated; the next
tabs update guarantees it shrinks back to the natural body-scrollHeight.
Tab-group drag-and-drop: group chips are now valid drop targets in the
same drag session as tab reorder. Dragging any tab onto a chip and
dropping assigns that tab to the chip's group (which also auto-clusters
it via the existing tab-group handler in main). Chip highlights acid
green while a valid drop hovers.
Collapsed group vertical popover: click a collapsed group chip and a
floating panel opens below it, listing every tab in the group. Each
row shows the favicon + title + a ✕ to close that tab. Clicking a row
switches to the tab AND expands the group so the newly-active tab
appears in the strip (tabGroupToggle). Popover closes on outside click
or Escape; the outside-click filter also ignores clicks inside .gchip
so opening the popover doesn't immediately close it.
growChromeForMenu() and closeAllMenus() now include .grouppop in their
overlay queries so the popover contributes to chrome-view sizing and
gets cleaned up alongside the ctxmenus.
Deployed: scp + sia-upload of both trees. Verified LIVE 0.3.5.
Setup 3d6d3d6aeacf482284707f50d129315d07a593e09b5d68af852abe5aa0ed4c92
Portable 6c8c5ef212bfccb377e17404e83a18cf2f09771d0eb3e0ec86a85587efdcfc9e
Three address-bar suggestion fixes.
Per-row ✕ delete on hover. Clicks on the X call address-forget instead
of address-pick; the row disappears optimistically in the picker and
main drops the entry from history + persists. Sender-URL gated to the
picker's own file:// origin.
Enter with a highlighted suggestion now navigates to THAT url. The
URL input handler tracks a pickerHasCursor flag that flips true on
ArrowDown/ArrowUp and false on any input; Enter with cursor forwards
to the picker's own submit path via addressCursor("enter") — before
this fix, Enter always ran goURL against the typed letters, which
submitted them as a web search instead of opening the selected url.
Address bar reliably shows the picked URL. onAddressPicked now arms
an overrideUrlBarUntil = now+1500ms flag; the onTabs handler treats
that window as "force write the url", bypassing the focus-guard that
was leaving the bar blank when blur() hadn't landed yet.
Deployed: scp + sia-upload of both trees, verified LIVE 0.3.4.
Setup e016d66543976b7a4df5ac16033319d59cb0f418e54d7e838ef16de274525a83
Portable b9881a62c39ecfe22644611f5aa16a48f32f97f9ec5751b5888ccbd870338c3e
Three 0.3.2 misses fixed:
Tab context menu was clipped by the chrome view's fixed height — the
menu rendered with position:fixed inside a WebContentsView whose
bottom edge stops at CHROME_H, so items below the tab strip weren't
visible. New growChromeForMenu() measures every open menu's bounding
box and calls T.setChromeHeight(needed) so main.js grows the chrome
view to fit; closeAllMenus() calls syncHeight() to restore.
Bookmarks bar was hidden on every tab except Home, so Add-to-
Bookmarks had no on-screen feedback. Now always visible.
Tab groups now actually cluster. Assigning a color to a tab moves it
adjacent to its group-mates in the tabs array (main-side splice, no
render-only trick). The chrome renderer inserts a colored group chip
before the first tab of each group; clicking the chip toggles a
per-group collapsed state (in-memory, resets on relaunch). Collapsed
groups hide their member tabs and the chip shows the member count.
Expanded groups keep the per-tab top color stripe from 0.3.2 so the
cluster reads as one visual block.
Deployed: scp + sia-upload, verified LIVE 0.3.3 on dl.silentmode.st.
Setup 09331b2fd9ccf136e2183b7cd85354cfd56e2ed50260b7aadeed63c7ea450251
Portable 21752d0fc85fb39ec1e65192920461e9ae395a22d9a68abd27f12e638d0fdd07
Right-click a tab: floating context menu with Reload, Duplicate, Group
(submenu: None / Red / Orange / Yellow / Green / Cyan / Blue / Purple),
Add to Bookmarks, Mute (also Unmute; 🔇 shows next to the title when
muted), Close. Menus close on outside click or Escape.
Group state is per-tab. A grouped tab shows a colored dot before the
title and a matching 2-px accent stripe on the top edge, so a cluster
of same-group tabs reads visually. Palette is drawn from existing
provenance colors (err/warn/acid/srv/sia/blue).
Backend IPCs are all tab-scoped (not "active tab"): tab-reload,
tab-duplicate, tab-mute (toggle or explicit boolean), tab-group,
tab-bookmark. emitTabs payload gains muted, group, and url so the
menu can read current state.
Installer wizard branding: 164×314 sidebar BMP with the compass mark
centered + "Theseus / NAVIGATOR" wordmark under it, plus a 150×57
top-strip header with a mini compass on the right. Sharp can't write
BMP directly (only png/webp/etc), so nsis/make-icons.mjs renders raw
RGB via sharp and wraps it in a hand-rolled 24-bit uncompressed BMP
header. Uninstaller reuses the same sidebar.
Silent-install fix: nsis/installer.nsh's AriadnePageCreate now checks
IfSilent BEFORE touching nsDialogs::Create. In /S mode the flag is
zeroed and the function returns cleanly, so the installer no longer
hangs waiting for a page it will never draw. This is why 0.3.2 needed
two builds — the first hung on /S install; the fixed hash is the one
that ships.
Deployed: scp + sia-upload of both trees. Verified VPS hash matches
local 09331b2f. Fresh /S install to D:\Program Files\Theseus Navigator\
placed 0.3.2 with the correct HKCU Uninstall registry entry.
Setup fe59105d2e99a41b7000caeb86601a8e1675846d193e92204034669f5b368d60
Portable 1b6eda55b53894cf9889548116c7b6100888fb160edc84cc1592bb79f9d95b53
The update flow no longer asks the user to click Download. When
checkForUpdate detects a newer version, autoDownloadUpdate() kicks off
session.defaultSession.downloadURL against the setup URL immediately.
will-download recognises the update URL and routes the file to a
fixed %TEMP% path (bypassing the visible downloads panel entirely),
streams updateDownloadReceived/Total into the chip via
emitUpdateAvailable, and flips updateDownloadState to "ready" when
the transfer finishes.
Chip states:
idle first render before the fetch starts — clickable to
trigger the manual download (kept as a fallback).
downloading "↓ 42% — 0.3.2" — no click, just progress.
ready "✓ Install 0.3.2 & restart" — one click.
failed fall back to the pre-0.3.1 explicit-download click.
install-update-now IPC: spawns the cached setup with /S (detached,
stdio ignored), then app.quit() 400ms later so the installer can
overwrite the running exe. Our nsis/installer.nsh detects an existing
Ariadne install via the HKLM registry and skips its Ariadne prompt on
upgrades, so the /S run is fully unattended.
The one-click flow eliminates two long-standing sources of confusion:
- "Download opens a different browser" — Theseus's default session
fetches the installer itself, not a URL handoff to shell.
- "Update requires multiple wizard clicks" — /S skips them.
Extensions aren't touched by this. The framework lives in
addons-host.js + sidebar-preload.js; add-ons themselves live in
%APPDATA%\Theseus Navigator\addons\<id>\ and are a separate layer.
New extensions ship by drop-a-folder, no browser release required.
Deployed: scp + sia-upload, verified 200 + 0.3.1 in the manifest.
Setup bb9d8dec124b8e2a976642ed77051b04edd1d03185581f99db845ef168919836
Portable 1e2d02aea13656574c374440b0c41374290f1bbf85ac6313cec075292cfc34f8
Home cards decoupled from the installer. Theseus now fetches
https://dl.silentmode.st/home-cards.json at boot and every 6h, caches
into <userData>/home-cards-remote.json, and applies it as the default
tier. Priority in loadHomeCards() is:
1. <userData>/home-cards.json (user edits — unchanged, always win)
2. <userData>/home-cards-remote.json (last successful fetch)
3. DEFAULT_HOME_CARDS (code fallback for offline first-boot)
On a successful refresh, main pushes the new list to every home tab
via the home-cards IPC — but only when the user has NO local edits.
The user's edit mode remains authoritative.
Content updates now happen by editing site/home-cards.json and running
scp + sia-upload. No browser build required.
Prometheus.X copy fix: "Decentralized App Marketplace" (was
"marketplace for BCDN-hosted apps" — the marketplace isn't BCDN-
specific). Applied to both DEFAULT_HOME_CARDS (fallback) and
site/home-cards.json (canonical live copy).
Deployed:
- scp installers + manifest + tools/ + releases/ + home-cards.json
to /opt/silent-mode/dl/ and /opt/silent-mode/site/
- sia-upload of both site trees
- verified home-cards.json served 200, manifest 0.3.0 live
Existing installs of 0.2.5 will surface 0.3.0 in the update chip and
also pull the new home-cards.json — so the "Decentralized App
Marketplace" wording lands on ANY current install (0.3.0+) the moment
this commit deploys, without waiting for anyone to install 0.3.0.
Wait — 0.3.0 is what CARRIES the fetch logic. So the auto-pull benefit
only kicks in from 0.3.0 onward. Users on 0.2.5 or earlier need to
install 0.3.0 once; from then on every home-card edit reaches them
without a new browser release.
Setup 50be20d6e3811445817e35c560dce1a4bbcc62004d6b0480848a211a654319ea
Portable 021afe629c61f2f49e95f936261649bdcff58c8b79bd0d903a8d10e6009740b0
Home page card grid gets a brand pass. Each title's TLD suffix (.X,
.bch, ...) is now styled in acid (--acid) with a soft text-shadow
glow. styledTitle() splits the domain on the last dot so capital
labels like "Theseus.X" render as ink + acid.
Card copy tightened per name:
hello.bch A small page on the blockchain itself.
siatest.bch A page with no server, backed by Sia.
SilentMode.X Infrastructure development for a decentralized web.
Theseus.X The Web Navigator - this browser's own address.
Sirius.X Register and manage BCDN names.
Hephaestus.X The forge - Silent Mode's code host.
Prometheus.X An app store - a marketplace for BCDN-hosted apps.
Helios.X Search engine for the decentralized web (in design).
Hermes.X Messaging - end-to-end encrypted over Nostr.
Badges: dropped the generic ".x" tag; each .X card carries its role
label instead. Palette assigns each role a distinct hue drawn from
the existing provenance colors, so the grid reads as families without
adding new brand values:
Infrastructure / Registrar acid
Navigator blue
Code host sia purple
App store srv teal
Search warn amber
Messaging err coral
Descriptions brighten from --mut to --ink on card hover so the ink
color reads as the "acid touch" the user asked for without adding
literal green blocks to every card.
Deployed: scp installers + manifest + tools/ + releases/ pages to VPS,
sia-upload of both trees, verified HEAD 200 + manifest 0.2.5.
Setup 7fd323a87bd32b780e147de18e16ecd82f89960bbd8e9a619c5d25d374597cd2
Portable 3166e64cf56badd7b26c4c793dc79bbed6f9d6c48fbd467d97f845385b91b1dd
Home cards: DEFAULT_HOME_CARDS replaced with the .x sibling grid the
user asked for -- hello.bch, siatest.bch (the "types of BCDN" pair),
then silentmode.x / theseus.x / sirius.x / hephaestus.x /
prometheus.x / helios.x / hermes.x. Existing installs with a saved
home-cards.json keep their edits (defaults only seed fresh profiles).
Search engine picker sync: user reported the toolbar dropdown listed
engines as active that Settings > Search showed differently. Root
cause: settings.searchEngine could be pointing at an id not in the
currently-enabled set (stale settings.json after DEFAULT_ENABLED
changes across versions). loadSettings now normalizes on boot -- if
searchEngine isn't enabled, fall back to enabled[0]; and
installedEngines gets unioned with enabledEngines so the two lists
can't disagree in ways that make toolbar and Settings render
different rows.
Proxy auth support in the framework: setSessionProxy accepts
`{ proxyRules, auth: { username, password } }` or an inline
`socks5://user:pass@host:port` URL. When creds are present, the
handler strips them from the URL, installs a session#login listener
on the default session that answers with them, then calls setProxy.
Chromium's SOCKS5 client doesn't consume proxy auth (known Chromium
limitation), but HTTP proxies work; SOCKS-based extensions need to
gate by IP allowlist at their server. Log line masks the password.
Update chip note: the "download opens in a different browser" was
0.2.0-era behavior. 0.2.1 rewired it to session.downloadURL. Anyone
still seeing it needs to install 0.2.1+ once.
Deployed: scp + sia-upload both trees, verified HEAD 200 + manifest
0.2.4 live.
Setup 3abaeff73afcecc9e4f05f749765e168a97a377890211dbae4345654c84ce2f1
Portable 71017563d6ba107cb25e24be240abcb78e34491d8f7aa42e095421edbc482a85
Brand: the red-N compass from theseus.x is now the taskbar / titlebar /
File Explorer icon everywhere. Source SVG lives at
site-theseus-x/assets/favicon.svg so brand + browser icon stay in sync.
nsis/make-icons.mjs renders it to build/icon.png (512x512) and
build/icon.ico (multi-resolution: 16/24/32/48/64/128/256). Wired into
package.json: build.win.icon, build.nsis.installerIcon + uninstaller
+ header; the ico ships as an extraResource so main.js's BrowserWindow
also uses it at runtime. Icons live in build/ which is gitignored -
run `node nsis/make-icons.mjs` when the SVG changes.
Sidebar: multi-panel picker strip. When 2+ extensions register sidebar
panels, sidebar-preload.js injects a 32-px tab strip at the top of
every panel's document. Click a tab -> ipcRenderer sidebar-open ->
loadFile switch. Preload also injects box-sizing:border-box + a
33-px body padding so height:100% panels don't overflow. Solo-panel
case is unchanged (strip only appears when panels.length >= 2).
Search: Startpage is the new default. Both the default id and the
enabled-list ordering put it first. DDG stays enabled by default too.
Deploy: scp installers + manifest + tools/ + releases/ to VPS,
sia-upload of both trees, verified HEAD 200 and manifest 0.2.3.
Icons regenerated from theseus.x's favicon.svg; the .svg itself
shipped in the 0.1.x window when the theseus.x site went live.
Setup 972f6209639122f32f032d5f2f9fc5a4808e0d4a810f88ae38ec9a1275ac52ac
Portable a9771f054ec36b9aff6ddee958cecf7e84cdacd4d7932aa8385c445aab4d29be
User-visible rename: the Settings tab and its labels say "Extensions"
now instead of "Add-ons". Internal identifiers (disabledAddons, the
addons/ folder, IPC channels, capability strings) stay put — code
churn wasn't worth it, and users only see the user-facing text.
Draggable sidebar. sidebar-preload.js now injects a 5px grip strip
along the LEFT edge of every panel document. mousedown+mousemove
streams delta-x px to main via sidebar-drag IPC; main clamps to
[200, 800] and debounces a save to settings.sidebarWidth. Width is
restored on next launch. The default is still 340. Faint acid-green
highlight on hover so the affordance is discoverable.
New extension capability: session-proxy. An extension whose addon.json
declares "session-proxy" gets api.setSessionProxy(rules) which routes
to session.defaultSession.setProxy — the same primitive Tor already
uses under the hood. Rules can be a string ("socks5://host:port") or
an object matching Electron's setProxy shape; null clears. The
capability is opt-in: an extension without the declaration gets an
error if it tries to call setSessionProxy. This is the framework
surface a private 3-VPS relay extension would build on (extension
folder stays on the operator's disk only; nothing about it appears in
the public build).
Deployed: scp installers + manifest + tools/ + releases/ pages to
VPS, sia-upload of both trees, verified HEAD 200 and manifest 0.2.2.
Setup 67e3cda1b76a6dba966d91a43e78fdb52378f860c0f75a7775d11b96e1b4f5e3
Portable 415c804f75df6fab5072650fbdc3cffa4124ed02f8ff53a91281ebd2dc4775c3
Update chip: the Download button used to hand the URL to the system
browser (shell.openExternal) - user opens Theseus, gets prompted to
update, clicks Download, watches ANOTHER browser wake up and start
the transfer. That's the "why did a different browser open?" report.
Fix: session.defaultSession.downloadURL(url) - triggers the same
will-download handler our own downloads panel already listens on, so
the file lands in the user's Downloads folder AND appears in
Theseus's downloads chip with progress + Show-in-folder. Rejected
protocols unchanged; only dl.silentmode.st and silentmode.st URLs
survive the allowlist.
Ctrl+B toggles the add-on sidebar (matches VS Code). Verified end-
to-end against a fresh <userData>: bundled Notepad was seeded,
Ctrl+B opened its panel, typed text autosaved to
<userData>/addons-data/notepad.json, restart re-mounted and the
text loaded back before the append could clobber it.
Deployed: scp installers + manifest + tools/ + releases/ to VPS,
sia-upload of both trees, verified HEAD 200 and manifest 0.2.1.
Setup c5202ddba5a4bf4cd8d71c665c72bdd996ced761f9a7aae295cb960e17988f47
Portable 9142764f78e41c6a6220960d5f7278e0abe6c1c91e5033871d95225355d8feda
0.1.x -> 0.2.x — two new subsystems land together. 0.1.3 (which had
only the error pages) is retired; those hashes never went live, so
we jump 0.1.2 -> 0.2.0 for clean version->hash 1:1.
Add-on framework:
- Discovery scans <userData>/addons/<id>/; each add-on carries an
addon.json manifest and an activate(api) CommonJS entry. Nothing
about a private add-on ships in the public build - drop the
folder, restart, it's live.
- Bundled 'Notepad' reference add-on ships in resources/bundled-addons/
and is seeded into the user's addons dir on first boot. Right-
sidebar textarea, autosave, char/word counter.
- Extension point in this rev: sidebar-panel (WebContentsView on the
right, SIDEBAR_W=340). Toolbar toggle appears only when at least
one add-on registers a panel.
- Settings gains an Add-ons tab: list installed, per-add-on enable/
disable toggle (settings.disabledAddons persists), Show folder /
Open add-ons folder / Reload buttons, trust-model warning.
- Origin-gated per-addon storage: main derives the add-on id from the
sender file:// URL, so a panel can only touch its own kv store
under <userData>/addons-data/<id>.json.
Branded error page:
- did-fail-load on every tab -> error.html with kind = name-not-
registered (BCNR host + ERR_NAME_NOT_RESOLVED) | name-unreachable
(non-BCNR host + NAME_NOT_RESOLVED) | unreachable (conn refused/
reset/timeout) | tls (cert range) | generic.
- Actions per kind: Retry + Home always; Search for '<host>' and
Register-on-Sirius where a host is present; Tor guide link on
unreachable.
- Address bar keeps the failed URL (refreshTabUrl already skips
file://) so the user can edit and retry.
- Ignorable codes (-3 ABORTED, -20 BLOCKED_BY_CLIENT, subframe
errors, internalNav) skipped so normal user actions don't paint
an error page.
Deployed:
- scp installers + manifest + tools/ + releases/ pages to VPS
- sia-upload of ../site (silentmode.bch) and ../site-theseus-x
- verified HEAD 200, manifest reads 0.2.0 2026-08-31
Existing 0.0.6/0.1.2 installs will surface 0.2.0 in the update chip
on next launch.
New subsystem for extending Theseus with folders on disk. Each add-on
lives at <userData>/addons/<id>/ with an addon.json manifest and a
CommonJS entry that exports activate(api). Nothing about a private
add-on ships in the public installer - drop the folder, restart, it's
live. Bundled reference add-ons ride in the packaged app under
resources/bundled-addons/ and are seeded into <userData>/addons/ on
first boot; the framework treats seeded and drop-in add-ons the same.
Files:
- addons-host.js Loader + api.registerSidebarPanel() + per-
addon storage on <userData>/addons-data/.
Kept at the CommonJS-scoped top level (lib/
is ESM-scoped via its own package.json).
- sidebar-preload.js Runs in every sidebar panel. Exposes
window.silentmode.storage.{get,set,all} +
onVisibility. Main-side handlers derive the
add-on id from the sender file:// URL, so a
panel can only touch its own store.
- bundled-addons/notepad/ Reference add-on: addon.json, index.js,
note.html. Autosaving textarea with char /
word count.
main.js:
- Extension point: sidebar-panel. One right-anchored WebContentsView
(SIDEBAR_W=340) hosts the current panel; layout() shrinks the tab
views by the sidebar width when visible. First registered panel
wins for MVP; picker for multiple panels lands later.
- initAddons() at app.whenReady(): seedBundledAddons, then
AddonHost.discoverAndActivate.
- IPC surface: sidebar-toggle / sidebar-open / sidebar-close /
sidebar-state, addons-list / addons-set-enabled / addons-reveal /
addons-open-dir / addons-reload, and origin-gated
addon-storage-get/set/all.
- Settings gains `disabledAddons: []` — off-toggled ids persist and
the loader honours them without a restart (discoverAndActivate
runs again on toggle).
chrome.html: toolbar sidebar-toggle button, hidden until at least one
add-on has registered a sidebar panel.
settings.html: new "Add-ons" section under privacy. Lists installed
add-ons with icon / name / version / description / capabilities;
per-add-on enable/disable toggle + Show folder button; page-level
Reload and Open add-ons folder buttons; warning note about the trust
model.
package.json: build.files gains sidebar-preload.js + addons-host.js.
extraResources gains bundled-addons/ so the packaged app carries the
reference notepad for the first-boot seed.
Verified: `npm start` boots, addons-host discovers the notepad,
activates it, registers one sidebar panel. Log confirms
"1 installed, 1 enabled, 1 sidebar panels". Actual sidebar rendering
+ notepad UI need clicked-through validation on a real install.
Not shipped yet - deploy still blocked on the fail2ban VPS SSH ban.
Ships as 0.2.0 once SSH clears (this is a new subsystem, not a fix).
Setup f2afc14efc63008cbb9dad44176e94146386db4c0afda4459f1d4eb929172b6d
Portable 5d08b1415526934db8de780949a610896064fe9567aa0e5e1702ebabd7eb7df2
Chromium's default 'This site can't be reached' replaced with a Theseus-
themed error page. did-fail-load on every tab's webContents (main frame
only, non-ignorable code) routes the tab to error.html with the
attempt URL, host, error code, and description as query params. The
page keeps t.url pointing at the failed URL so the address bar shows
what the user typed and they can edit + retry - refreshTabUrl's
existing file:// skip means the error page's own path never leaks
back into the bar.
Five kinds, chosen by pickErrorKind(code, host):
name-not-registered BCNR-eligible host + ERR_NAME_NOT_RESOLVED.
Says "no BCDN record on chain, no clearnet host
either." Offers Register on Sirius + Search +
Retry + Home.
name-unreachable ERR_NAME_NOT_RESOLVED on a non-BCNR host. DNS
failed - offers Retry + Search + Register +
Home.
unreachable CONN_REFUSED/RESET/TIMED_OUT/CLOSED/NETWORK_CHANGED.
Offers Retry + Tor guide + Home.
tls ERR_CERT_* range (-200..-299). Offers Retry +
Home.
generic Everything else.
home-preload.js gains `window.errorpage` alongside `window.home`. Both
APIs are sender-URL-gated in main - a random page seeing the shape
can't invoke them (isErrorPageSender / isHomePageSender). The external-
open handler additionally allowlists Silent Mode domains only.
package.json build.files gets error.html + error-preload.js so
electron-builder actually bundles them (GOTCHAS rule: an unlisted
runtime-loaded file silently opens blank).
Ship pages (releases-manifest.json, tools/index.html, releases/index.html,
site-theseus-x/index.html) updated to 0.1.3 with the new hashes.
DEPLOY STATUS - blocked on VPS SSH: my IP was hit with a full-port ban
mid-turn (likely fail2ban from the burst of scp during the 0.1.0-0.1.2
iterations). Site pages/manifest/installers are committed locally but
NOT yet on dl.silentmode.st or the Sia mirror. Live still reads 0.1.2.
User needs to unban 195.184.247.106 on their end, or wait for the ban
to expire, before the ship pages match reality.
Setup b8cc85ec0f70844e4ce14a70b0ff89b9d9720be2f0e5568e56c51ecef706b14d
Portable 6540b4c67906046cd2c5303a9ccaa4e00a39b3daa5792b71e16c4f141f498220
Copy-only change to the installer's Ariadne page: names the mechanism
BCDN — Bitcoin Cash Domain Names — in the body text instead of listing
example TLDs. Reads more clearly to a first-time visitor who might not
know that ".bch" and ".x" are the same registry.
Everything else from 0.1.1 rides along: dedicated wizard-page checkbox
for Ariadne's Thread (pre-checked, skipped on upgrades), uninstall
symmetry, Startpage POST-form fix.
Deployed: scp installers + manifest + site pages to VPS, sia-upload of
both trees, verified 200 + manifest 0.1.2 2026-08-31.
Setup 2a2784943006b0c4f52e22cc2ec51a43e8007497d90da022798e739d83f805f0
Portable 1d9cfa6467d66604ef2f672969afdc288f46608913c35658f9c7466820ab01df
Ariadne install UX: promoted from an end-of-install MessageBox popup to
a dedicated wizard page. nsis/installer.nsh replaces the MessageBox
customInstall with a `Page custom` (nsDialogs) that shows FIRST in the
install flow (electron-builder's INCLUDE hook is placed above its own
MUI_PAGE_* inserts and NSIS processes page directives in file order —
that is the earliest hook we have without editing the template).
Framed as "Options" so it reads naturally as a preamble.
The page:
Header: "Optional add-ons"
Body: A 4-line explanation of Ariadne's Thread and the trade-off.
Checkbox: "Install Ariadne's Thread (recommended)" — pre-checked.
Footer: Notes the UAC prompt and that Theseus install continues.
Skipped entirely (Abort) if Ariadne is already installed. State is
saved from AriadnePageLeave into $InstallAriadneFlag; customInstall
runs the chain-installer iff BST_CHECKED.
Uninstall side unchanged (still MessageBox — a full custom uninstall
page would be overkill for one Y/N during a rare event).
Also carries 1e5d670: don't re-route same-origin navigations through
navigateTab. Was silently breaking every site whose in-page form
POSTs — Startpage's search box submits to /do/search via POST, and
navigateTab -> loadURL always GETs, dropping the body. Same-origin
navigations now stay on Chromium's native path.
Deployed: scp + sia-upload of both trees. Verified HEAD 200 on the
installer, live manifest reads 0.1.1 2026-08-31, VPS hashes match.
0.1.0 (never deployed for long — same-day iteration) is preserved
in _prev/ for rollback.
Setup 510f81fd5e9b4287a55817aaf5080e5d46005eecb5359eb06892040cf8d10d87
Portable 96bfb469f2d9dfe11606aa10db92febdfce73df980eec475ab285f2334925158
Bundled AriadneResolver-Setup-0.1.0.exe (9ab725be, unchanged 0.1.0)
Version 0.0.9 -> 0.1.0 marks the "Silent Mode = one install" milestone:
Theseus and the system-wide Ariadne resolver now ship together.
nsis/installer.nsh (electron-builder `nsis.include`) hooks two macros:
customInstall -- If Ariadne's Thread is NOT already installed,
MessageBox MB_YESNO|MB_DEFBUTTON1 asks whether
to install it now. On Yes, chain-invokes the
bundled Inno installer with
/VERYSILENT /SUPPRESSMSGBOXES /NORESTART
(Ariadne raises its own UAC because Inno declares
PrivilegesRequired=admin). Ariadne being present
already suppresses the prompt on upgrades.
customUnInstall -- Reads Ariadne's UninstallString from HKLM 64-bit
Uninstall\{...ARIADNERSLVR}_is1. If present,
symmetric MessageBox asks whether to remove it
too, then ExecWaits the string with the same
silent flags. Skips silently if Ariadne isn't
installed.
Registry probe uses SetRegView 64 (Ariadne installs 64-bit per
ArchitecturesInstallIn64BitMode=x64compatible in its .iss). Reset to
`lastused` after each probe to leave electron-builder's own state
unchanged.
Package changes:
- version 0.0.9 -> 0.1.0
- build.extraResources adds build/AriadneResolver-Setup-0.1.0.exe
(unpacked to $INSTDIR/resources/); build/ stays gitignored - the
binary is regenerated by copying from AriadneResolver's Inno output
- build.nsis.include -> nsis/installer.nsh
Size: installer 122 MB (+25 MB for bundled Ariadne), portable 122 MB.
Portable also carries the Ariadne exe as a side effect of extraResources
being global; portable can't invoke an installer, so the 25 MB is dead
weight there - future rev could scope extraResources per-target.
Site updates: tools page + releases page + theseus.x landing all
reflect the 0.1.0 hashes, sizes, and the new "Ariadne's Thread bundled"
line. tools page's "Coming to the Windows installer" footnote is
rewritten past tense because it landed.
Deployed:
- scp installers + releases-manifest.json to /opt/silent-mode/dl/
- scp tools/ + releases/ index pages to /opt/silent-mode/site/
- sia-upload of ../site and ../site-theseus-x
- verified HEAD 200, manifest reports 0.1.0 2026-08-31, VPS hashes
match local
Existing 0.0.6 (or newer) installs will surface 0.1.0 in the update
chip on next launch.
Setup 5452a34f211176cf04b7ea77a70fb838f0031b6ea0faed2cd270eae989b20c60
Portable ee602a016caf022452b2096ca683cf4bb4cea93ded1352192910c581a962d9ae
Version bumped 0.0.8 -> 0.0.9 because the prior 0.0.7 and 0.0.8
"Ship ..." commits (f464de6, 1a3b232, ed9ec18) never actually
deployed - the live manifest at dl.silentmode.st still served 0.0.6
until this commit. Bumping the number preserves version->hash 1:1:
this 0.0.9 is the FIRST post-0.0.6 binary that ever went public.
Cumulative content:
- 0.0.7 warm-start from bundled BNS snapshot + 30-s background delta
refresh + Sia snapshot pull on boot (first .bch page opens
near-instantly, index stays fresh while browser runs).
- 0.0.8 window.bcnr read-only dApp API on every page (resolveName,
isRegistered, getBcnrTlds, getRecordVersion). Permission
origins now scope to eTLD+1 via the Public Suffix List
(same rule Chromium uses for cookies/storage).
- 0.0.9 Address-picker paints the picked URL in the address bar
immediately (was leaving the typed prefix while the target
loaded). Reload keys wired end-to-end: F5 / Ctrl+R soft,
Ctrl+Shift+R / Ctrl+F5 hard, Shift-click the toolbar reload
does hard too.
Deployed:
- scp installers + releases-manifest.json to /opt/silent-mode/dl/
- scp tools/ + releases/ index pages to /opt/silent-mode/site/
- sia-upload of ../site to bns/silentmode/
- verified: dl.silentmode.st HEAD 200 on the installer, manifest
reports 0.0.9 2026-08-31, VPS hashes match, both silentmode.st and
silentmode.bch tools pages show 0.0.9 buttons.
Existing 0.0.6 installs will surface the update chip on next launch.
Merges a parallel session's work with the multi-source BNS story from 0.0.7.
The dApp side (parallel session)
--------------------------------
* bcnr-preload.js — installs `window.bcnr` on every page via contextBridge.
Read-only surface: resolveName(name), isRegistered(name), getBcnrTlds(),
getRecordVersion(name), plus getPermissionOrigin() for diagnostics. All
Promises; a missing name returns null (not throw). No signing, no wallet
unlock — that surface is designed but deliberately out of scope for 0.0.8
(see TheseusNavigator/DESIGN-integrated-wallet.md).
* bcnr-origin.js — pure function that computes the eTLD+1 permission origin
for a URL. ICANN suffixes via `psl` (same PSL Chromium uses, handles
.co.uk / .github.io / etc); BNS names key off the on-chain TLD list so
foo.wallet becomes a public suffix as soon as `wallet` appears there.
Match browser cookie / MetaMask semantics: a grant on pay.merchant.com
covers account.merchant.com but not evil.com.
* dev/bcnr-selftest.js, dev/origin-selftest.mjs — self-tests, no I/O.
* main.js wires bcnr-preload.js into session.defaultSession.setPreloads() so
it runs BEFORE per-WebContentsView preloads; adds bcnr:* IPC handlers.
* preload.js + chrome.html — small hooks so the shell picks up window.bcnr
the same way regular content does.
* package.json — psl dep, bcnr-preload.js/bcnr-origin.js in `files`.
Also included
-------------
* AriadneResolver/mobile/.../UpdateCheck.java — in-app update-check for the
Android app; already active in the shipped 0.11 APK (build.ps1 -Recurse
picked it up), formalising the source now.
* TheseusNavigator/snapshots/bns-name-snapshot.json — refreshed bundled
starter (73 beacon txs, root c37b8596…c54e414ba).
* Site pages + manifest updated to point at 0.0.8.
TheseusNavigator-Setup-0.0.8.exe 95.4 MB
21939743eafdfe8742a6b7c4b987bd2782384d7bc41289cb80a7e08019dc9f02
TheseusNavigator-0.0.8-portable.exe 92.7 MB
2aa429fe39dc0fa4ac040fc6d6eb31b0f890c8a83175c49fcb50f052c480d39d
The first .bch page now opens with zero user-visible latency and stays fresh
for as long as the browser runs. Four sources conspire in parallel — none of
them can block a navigation:
1. Warm start from disk (sync)
warmFromSnapshot loads the user-cache snapshot first, then falls back to
the copy bundled with the build. sharedIndex is set BEFORE the first
navigation can even fire.
2. Continuous background delta refresh (every 30 s)
startBnsPolling opens one electrum connection, fetches the beacon
history (a single call), and only pulls verbose tx bodies for txids we
don't already have — mergeFreshHistory-style. Merges into memory,
rebuilds the index locally, persists the enlarged snapshot to disk.
Turns a ~60 s full walk into ~1 s per new event.
3. Sia snapshot pull on boot (one-shot, wins the NEXT boot)
refreshSnapshotFromSia downloads the operator's published snapshot from
s3.silentmode.st for the next launch. After a long idle period the
browser resumes from that fresher snapshot instead of walking days of
events.
4. ensureIndex full-walk fallback
Still runs on boot for the case where there's no bundled snapshot AND
the poll hasn't landed yet — very first launch, offline install, etc.
resolveHost now prefers sharedIndex (which the poll keeps live) and, on a
miss with a stale index, triggers pollAndMerge (delta fetch) instead of
ensureIndex (full walk). The old full-walk fallback is only taken if the
delta primitives aren't available (running against an older resolver-web).
Argus/src/lib/resolver-web.js exports connectElectrum so the Theseus poll
can drive its own ad-hoc queries against the pool without duplicating the
Electrum wrapper.
Bundled starter snapshot refreshed: 73 beacon txs, root c37b859682…54e414ba.
Artifacts:
dist-public/TheseusNavigator-Setup-0.0.7.exe (95.4 MB)
sha256 1b0dabcd2b13067aa1fd89c3271708b35ba22dfc7a620e248b35e0487b7a104f
dist-public/TheseusNavigator-0.0.7-portable.exe (92.7 MB)
sha256 706dc01b21a88d3c2fbc3eeced0cab6ace873630bf3edc2d40915ca5be5c8cae
Four independent pieces of one story — make cold-start .bch resolution fast,
survive individual electrum outages, and turn adding a Silent Mode BNS server
into a checklist run.
* Argus/src/lib/snapshot-name.js — pure, interpretation-free snapshot format
for the name beacon (raw history + verbose tx cache + deterministic root).
* Argus/src/publish-name-mirror.mjs — mirror the snapshot to Sia S3
(floating + content-addressed) and Nostr NIP-33 (kind 30078,
d-tag bns-name-list). Same pattern as publish-tld-mirror.mjs.
* Argus/src/snapshot-name-to-file.mjs — dump the snapshot to a file for
bundling. Writes to TheseusNavigator/snapshots/bns-name-snapshot.json.
* Argus/src/lib/resolver-web.js — new export buildIndexFromSnapshot() runs
the same REG/UPD reduction as buildIndex() but from a pre-fetched snapshot,
no electrum required. Warm-start path in Theseus depends on this.
* TheseusNavigator/main.js — warmFromSnapshot() and refreshSnapshotFromSia()
wired into app.whenReady(). First .bch navigation returns from a warm index
instead of waiting on a full electrum walk.
* TheseusNavigator/package.json — bundles the starter snapshot as an
extraResource so packaged builds ship with a floor.
* TheseusNavigator/snapshots/bns-name-snapshot.json — first starter snapshot
(70 beacon txs, root 8cc859aa…).
* VPS/BNS-SERVER-PLAYBOOK.md — turn-key checklist for spinning up a second
or third Silent Mode BNS server, distilled from what actually worked on
the silentmode box (BCHN 29 [chip]-not-[chipnet], multi-beacon indexer,
wss:// nginx, on-chain publish via electrum.bch, common gotchas table).
* Argus/src/register-electrum-list.mjs — publish the current chipnet
server pool as `el` on electrum.bch. Fits inside the 200-byte OP_RETURN
cap by sizing the record (drops fallback IP pins in favour of URL-only
tokens until they fit). NAME was chosen deliberately: two-part `electrum.bch`
normalises to the bare distinct name `electrum`, whereas the earlier
candidate `electrum.silentmode.bch` collapsed to silentmode.bch and would
have inherited/overwritten that name's records.
* Argus/src/revert-silentmode-bch.mjs — one-off recovery from the earlier
register-electrum-list revision that hit exactly that subdomain-collapse
bug (broadcast an UPD to silentmode.bch and clobbered its s3 record).
Records recovered from the local snapshot cache; safe to leave in the
tree as documentation of what happened.
Setup b740cb033173622b192701ff36f20a97e357fedebf77c3c7ead03d113a9ac229
Portable 782a2ad235a4a1e1491df6a1d16c817fa8542e363e3c6b139c7e04f40846f4c2
New:
- Tabs carry the page favicon (page-favicon-updated -> emitTabs -> the
chrome renders it in the same slot the loading spinner uses).
- Image right-click: Open image in new tab / Save image as... / Copy
image / Copy image address. Save-image-as uses the existing
will-download tracker, so it also shows in the downloads chip.
- Tab cursor: default arrow at rest instead of the always-'grab' hand.
Grab only shows while a tab is being dragged for reorder.
Deployed:
- scp installers + releases-manifest.json to /opt/silent-mode/dl/
- scp tools/ + releases/ index pages to /opt/silent-mode/site/
- sia-upload of ../site to bns/silentmode/
- verified HEAD 200 + manifest 0.0.6, VPS hashes match, tools page
shows 0.0.6 buttons on silentmode.st AND silentmode.bch (Sia mirror).
The 0.0.5 update chip will surface 0.0.6 on existing installs at next
launch (6h manifest poll).
Setup 606529ea9f1de9dc1ec5012534dda31b06919245f19f934ee1b9f4f10385a5bc
Portable ed89547a155e998278beccf21f9807841ac6267f75cdcbbead81b80acfee0adc
Fixes:
- Classic form-GET search engines (Google, Brave, Bing, Startpage,
Yandex, Ecosia, Mojeek, ...) now show results. The tab's
will-navigate intercept was dropping the query string on every
dotted-host navigation, so /search?q=foo landed on /search. DDG
only appeared to work because its in-page search uses pushState +
XHR and never triggered will-navigate.
- Address bar reflects the target URL immediately on Enter instead
of showing the previous page's URL until did-navigate fires.
New:
- Link-hover status bar: Firefox / Chrome-style pill at the bottom-
left of the window showing the href when the pointer is over a
link, hidden when it leaves. Auto-sizes to text, cleared on tab
switch, tracks window resize.
Also on-VPS: bns-gateway auto-index directory paths (f43ec61) so
silentmode.bch/tools/ returns the tools page instead of NoSuchKey.
Deployed:
- scp installers + releases-manifest.json to /opt/silent-mode/dl/
- scp tools/index.html + releases/index.html to /opt/silent-mode/site/
- sia-upload of ../site to bns/silentmode/
- verified: HEAD 200 on both installers, manifest reports 0.0.5,
silentmode.st/tools shows the 0.0.5 buttons, Sia mirror ditto.
The 0.0.4 update-check chip should now surface itself for existing
installs on their next launch.
Firefox / Chrome-style small pill at the bottom-left of the window
showing the href when the pointer hovers a link, hidden when the
pointer leaves. Fed by webContents.update-target-url on every tab;
auto-sizes to its text via an IPC resize channel.
Cleared on tab switch so a lingering hover pill from tab A doesn't
carry across to tab B. Positioned by layout() so it tracks window
resizes.
New files link-status.html + link-status-preload.js registered in
build.files (GOTCHAS.md rule: missing entries silently omit from
the packaged app).
Not shipped yet — bundles into the next Theseus release.
Bumps version so the chip actually surfaces itself on 0.0.3 installs
(the version-newer check requires a strict semver bump — same-version
rebuilds don't trigger the chip). From this release on, whenever the
manifest names a newer Theseus, users get a one-click download.
Mechanism
- main.js checkForUpdate() fetches https://dl.silentmode.st/releases-
manifest.json on startup (5s timeout, cache: no-store) + every 6h.
Finds the theseus-navigator release, compares version to
app.getVersion() with a numeric a.b.c comparator that handles
"0.10.0 > 0.9.9" correctly.
- On a match → stores { version, setupUrl, portableUrl, setupHash,
portableHash, date } and emits update-available to chrome. Cleared
after the user upgrades + relaunches (same-version → null).
- Re-emits on chrome's did-finish-load in case the fetch beats the
chrome view.
Chip UI (chrome.html)
- Acid-yellow pill between the downloads button and the Tor toggle:
"↓ Update to X.Y.Z" + a ✕. Main body opens setupUrl in the system
browser via shell.openExternal (origin-validated to
https://dl.silentmode.st/ or https://silentmode.st/). ✕ dismisses
for the current session — you'll see it again next launch if still
behind.
Trust anchor
- No signing / no cryptographic verification of the download in this
phase. releases.silentmode.bch publishes the SAME manifest URL, so
users who want to verify can cross-check the manifest hash against
what BCNR returns. The proper auto-updater with signature checks is
the follow-on to this cheap version.
Non-goals in phase 1
- No delta downloads; the user clicks and gets a full installer.
- No auto-install; download → user runs the installer themselves.
- No "check now" button in Settings; the periodic timer suffices.
- No portable-vs-installed detection; the chip prefers setupUrl (the
installer upgrades in place). Right-click for portable is future work.
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
Six user-visible improvements + supporting infra, all uncommitted from
the earlier session-in-progress state. Ships together in one release.
Chrome / tabs
- Same-size tabs: flex 1 1 0 with max 200px, min 60px. Container gets
overflow: hidden so many tabs shrink evenly instead of scrolling out.
- Drag-and-drop tab reordering. HTML5 drag events on each .tab; drop
side chosen by pointer x within target (Chrome UX). New move-tab IPC
splices the tabs array + re-emits.
Address bar
- Persistent history at userData/history.json capped at 500 LRU. Ranked
by host-prefix > url-prefix > contains > title-contains > recency.
- Floating suggestions dropdown (addressPicker WebContentsView) anchored
under the URL bar. Debounced 80ms input; ArrowUp/Down forward to the
picker via address-cursor IPC; Enter fires goURL; blur closes after
160ms so click-through registers. New files address-picker.html +
address-picker-preload.js. Cleared by existing clearHistoryOnQuit.
Password autofill (A.2 MVP)
- Green key chip in the address bar appears when the vault is UNLOCKED
and the active tab's host has matching credentials (exact hostname
match for phase 1; eTLD+1 upgrade queued as A.2.5).
- Click chip → floating picker of usernames. Click a match → main.js
runs a small script in the active tab: finds first visible
input[type=password]:not([disabled]), walks the same form for a
visible text/email/tel/url/search input whose name/id/autocomplete
matches /username|user|email|login|account|id/, fills both via the
native value setter + dispatches input/change so React/Vue-controlled
inputs update. New files pw-fill.html + pw-fill-preload.js.
- emitPwAvailability fires from pushNav + vault setup/unlock/lock so
the chip's visibility + count stays accurate.
Bookmarks bar
- Right-click context menu on the favorites bar. On empty area:
"Add current page" (or "Remove current page" if already saved). On a
specific bookmark: "Open", "Edit title…" (prompt), "Remove", plus
the add/remove-current entry. Uses a shared .ctxmenu style mirroring
the settings ctxmenu (dark/light aware).
- Empty-state text updated to mention right-click.
Home page
- Larger responsive card grid: auto-fill minmax(260-280px, 1fr) with
breakpoints at 600/900/1200. Cards have a subtitle line, a colored
badge (on-chain / Sia / server / custom), and edit affordances that
reveal only in Edit mode.
- User-editable set: Edit toggle reveals per-card ✎/✕ + a dashed "+ Add
card" tile. Modal for add/edit with title / URL / subtitle / badge.
Reset-to-defaults button.
- Persisted at userData/home-cards.json. New home-preload.js exposes
window.home = { getCards, setCards, resetCards, navigate }. IPC
handlers in main.js validate sender.getURL() matches our own
home.html — third-party pages see the API shape via the preload but
can't act on the user's local cards.
- Fallback set of 2 cards renders when window.home is unavailable
(e.g. opening home.html directly outside Electron for preview) so
the grid is never blank.
Docs
- TheseusNavigator/ROADMAP-identity-wallet.md — the phased plan for
the two independent strands (password manager A.2/3, browser wallet
B.1-6). Committed earlier this session; re-listed here for context.
- TheseusNavigator/SESSION-PROMPT-identity-wallet.md — pastable
kickoff for the next session picking up either strand.
Files added to build.files: address-picker.html,
address-picker-preload.js, pw-fill.html, pw-fill-preload.js,
home-preload.js.