Commit graph

99 commits

Author SHA1 Message Date
Local Dev
7aae2b2f3b feat(theseus/error): offline banner + BCNR did-you-mean on typos
Two problems bundled into the branded error page:

1) "fetch failed" on no network — the tab kept falling through to
   name-not-registered (and other "we asked and got nothing" verdicts)
   because a totally offline browser can't reach the beacon to know.
   Add a first-class `offline` kind that runs before every other
   verdict: navigator.onLine === false wins; the page says "You appear
   to be offline" and keeps the address so the user can Retry after
   reconnecting.

2) Typos on BCNR names (games.x when they meant game.x) — the page
   only offered Search / Register. Now the page asks main for the top
   near-matches from the warm sharedIndex (Levenshtein ≤ 2, same TLD)
   over an origin-gated `error-bns-similar` IPC. Up to three matches
   render as chip-links; clicking one retries at that host, preserving
   the original path. Works offline too — the index is local.

Origin-gate on the IPC uses the existing isErrorPageSender check, and
error-preload.js exposes only the invoke — no arbitrary index access.
2026-09-04 20:21:30 +02:00
Local Dev
c5dd03adc3 Ship Theseus 0.3.4 3d6d3d6a (address-picker delete X, Enter picks highlight, URL bar override)
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.
2026-09-02 04:28:54 +02:00
Local Dev
3bf41f92ae Ship Theseus 0.3.3 e016d665 (fix tab menu clip + always-on bookmarks + real tab groups)
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.
2026-08-31 21:29:14 +02:00
Local Dev
5ea4515085 Ship Theseus 0.3.2 09331b2f (tab context menu + branded installer)
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.
2026-08-31 19:30:30 +02:00
Local Dev
19ffde3bfa Ship Theseus 0.3.1 fe59105d (one-click updates: silent prefetch + install-and-restart)
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.
2026-08-31 18:47:50 +02:00
Local Dev
5d248541cf Ship Theseus 0.3.0 bb9d8dec (home cards decoupled from browser release)
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.
2026-08-31 18:38:09 +02:00
Local Dev
62d32573cf Ship Theseus 0.2.5 50be20d6 (home page brand pass: .X acid + role badges)
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.
2026-08-31 18:03:02 +02:00
Local Dev
454255e963 Ship Theseus 0.2.4 7fd323a8 (home cards refresh + engine-picker sync + proxy auth support)
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.
2026-08-31 17:24:39 +02:00
Local Dev
49dde73c6b Ship Theseus 0.2.3 3abaeff7 (new brand icon + multi-panel picker + Startpage default)
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.
2026-08-31 16:32:16 +02:00
Local Dev
ee0548fec9 Ship Theseus 0.2.2 972f6209 (Extensions rename + draggable sidebar + session-proxy)
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.
2026-08-31 16:00:34 +02:00
Local Dev
1a6b631d98 Ship Theseus 0.2.1 67e3cda1 (in-app update download + Ctrl+B sidebar)
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.
2026-08-31 15:35:47 +02:00
Local Dev
0117986657 Theseus: add-on framework MVP + Notepad reference add-on
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).
2026-08-31 13:51:08 +02:00
Local Dev
e43e009f73 Theseus 0.1.3: branded error page for load failures (BUILT, NOT DEPLOYED)
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.
2026-08-31 13:38:05 +02:00
Local Dev
6f4531cda0 Theseus: don't intercept same-origin navigations (unbreaks Startpage POST)
Startpage's in-page search form POSTs to /do/search. Our will-navigate
rewriter was calling navigateTab(id, url) which loads via loadURL(url)
- always a GET, no body. So the second search from within Startpage
always landed on /do/search with no query and no results.

Google's search form GETs, so 0.0.5's query-preservation fix was
enough for it. DDG uses history.pushState and never fires
will-navigate at all. Startpage POSTs, so it needs a different fix:
just don't intercept.

Only intercept CROSS-ORIGIN navigations for BCNR re-lookup. A form
submit or subpage link on the current site (same hostname) belongs to
Chromium natively - the site is already loaded from clearnet, its
subsequent navigation stays on clearnet. Preserves POST bodies for
every site, not just Startpage: any login form, comment submit, or
checkout flow that stayed on-origin was silently broken by the same
bug.
2026-08-31 05:13:20 +02:00
Local Dev
10cddcacca Theseus: address-picker URL bar + reload keys (F5 / Ctrl+R / Ctrl+Shift+R)
Address-picker fix:
Picking a suggestion from the address dropdown loaded the URL but
left the address bar showing the 3-4 letters the user had typed.
Root cause: onTabs's focus guard
  if (document.activeElement !== $("url")) $("url").value = d.url
skipped its write while the URL input still held DOM focus, and
clicking a sibling WebContentsView doesn't always deliver the blur
to the chrome renderer in time. Fix: address-pick sends an explicit
address-picked IPC to the chrome, which force-blurs and writes the
full picked URL before the tabs event arrives.

Reload keys:
None of the standard reload accelerators worked (Menu.setApplicationMenu(null)
drops Chromium's default menu accelerators, and hard-reload was never
wired at any layer). Now:
  F5 / Ctrl+R         -> soft reload
  Ctrl+F5 / Ctrl+Shift+R -> hard reload (reloadIgnoringCache)
  Shift-click on the toolbar reload button -> hard reload
Same before-input-event hook that carries Ctrl+Shift+M; always targets
the active tab regardless of which view got the key, and skips the
Settings tab. Toolbar tooltip updated to advertise Shift-click.
2026-08-31 02:50:57 +02:00
Local Dev
755e97b5bb Ship Theseus 0.0.8: window.bcnr dApp API + eTLD+1 permission origins
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
2026-08-31 01:38:55 +02:00
Local Dev
2fc428c220 Theseus 0.0.7: multi-source BNS with continuous delta refresh
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
2026-08-30 15:57:01 +02:00
Local Dev
af522f812a BNS: Sia+Nostr snapshot mirror, Theseus warm-start, VPS playbook, electrum.bch
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.
2026-08-30 09:59:01 +02:00
Local Dev
66394aed8c Theseus: tab favicons + calm cursor on tabs
Favicons:
- Each tab stores a favicon URL; wired via wc.on('page-favicon-updated')
  to Electron's first-emitted icon URL. Included in the emitTabs
  payload; the chrome renderer paints an <img class="fav"> in the
  same slot the loading spinner uses (spinner while loading, favicon
  once page-favicon-updated fires). No proactive clear on navigation
  - mainstream browsers keep the old icon until the new one arrives,
  which avoids a flash on every subpage click.

Cursor:
- .tab { cursor: default } instead of the previous 'grab'. Grab looked
  like the page was always in the middle of a drag. .tab.dragging
  keeps 'grabbing', so the grab hand only appears when the user
  actually picks a tab up to reorder it.
2026-08-29 23:29:18 +02:00
Local Dev
c6c0f16f9d Theseus: image context menu (open / save / copy)
Right-click on an image now offers:
- Open image in new tab   -> createTab(srcURL)
- Save image as...        -> wc.downloadURL(srcURL). The tab's
                             will-download handler leaves savePath
                             unset, so Electron shows the native
                             Save As dialog and the download appears
                             in the tracker.
- Copy image              -> wc.copyImageAt(x, y). Puts the decoded
                             image on the clipboard (paste into any
                             image-aware target).
- Copy image address      -> clipboard.writeText(srcURL).

Only shown when p.mediaType === "image" && p.srcURL, so text /
link right-clicks are unchanged.
2026-08-29 23:25:22 +02:00
Local Dev
574c0ed59a Theseus: preserve query on link-navigation intercept + immediate address-bar reflect
Two related navigation bugs, both surfacing when a page inside a tab
tries to submit a search:

1. will-navigate rewriter dropped the query string and fragment.
   Every dotted host went through
     navigateTab(id, parsed.hostname + parsed.pathname)
   which stripped ?q=... The classic-form-GET search engines
   (Google, Brave, Bing, Startpage, Yandex, Ecosia, Mojeek, ...) all
   silently landed on their /search endpoint with no query, so no
   results ever showed. DuckDuckGo only appeared to work because its
   in-page search uses history.pushState + XHR and never triggered
   will-navigate to begin with.
   Fix: pass the whole URL (minus scheme) so query + fragment survive.

2. Address bar showed the previous page's URL until the new page's
   did-navigate fired. navigateTab called setLoading() -> emitTabs()
   BEFORE assigning t.url, so the chrome renderer received the stale
   URL and painted it (goURL() blurs the input on Enter, so the
   "don't clobber typed text" guard didn't skip the write).
   Fix: assign t.url from host+rest immediately, before setLoading.
2026-08-29 22:22:47 +02:00
Local Dev
78d20d6197 Theseus: link-hover status bar (bottom-left href pill)
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.
2026-08-29 15:49:30 +02:00
Local Dev
cc563b0037 Theseus 0.0.4: cheap in-app update-check chip
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.
2026-08-28 20:21:44 +02:00
Local Dev
59d14d0e62 Hermes: optional bind to password vault (skip the second mnemonic prompt)
- password-vault: createVault takes { messengerRootHex } opt; unlockVault
  surfaces messengerRoot (null on legacy vaults, so nothing regresses);
  saveVault persists it; bindMessengerRoot mutates an unlocked state for a
  later attach-mnemonic-to-existing-vault flow.
- main.js password-setup: when the user provides a mnemonic, ALSO compute
  seedToPurposeRoot(seed, "messenger/0") and store it. Random-seed vaults
  stay as-is (no mnemonic = no messenger root to store).
- main.js hermes-init: two modes now — { mnemonic } (unchanged) or
  { useVault: true } (uses vaultState.messengerRoot directly, no mnemonic).
  Response includes source: "vault" | "mnemonic" for the panel to badge.
- main.js hermes-can-use-vault: cheap availability probe used by the panel
  to decide whether to show the vault sign-in shortcut.
- hermes.js: nostrKeyFromRoot(root) accepts 32-byte Uint8Array or 64-char
  hex; produces the same {sk, pkHex, npub} as nostrKeyFromMnemonic for the
  same seed (unit-verified: derivation paths converge on f2c92519...67f4).
- Messages panel: "Sign in with password vault" button appears above the
  mnemonic entry iff the vault is unlocked AND was set up from a mnemonic.
  The mnemonic path stays as the always-available fallback — bind is
  genuinely optional, not required.
2026-08-19 00:38:52 +02:00
Local Dev
7bd4b2b1c3 Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- 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
2026-08-18 20:14:43 +02:00
Local Dev
2be859267e Theseus UX batch: tabs, address history, password autofill MVP, home cards
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.
2026-08-17 02:17:12 +02:00
Local Dev
6f2d32b9d5 gateway+Theseus: pin BNS ip-record fetch to on-chain tls fingerprint
Uncovered by the 2026-08-13 subdomain-inheritance fix: once
`checkers.game.x` correctly picked the parent's `ip` record instead of
`s3`, the ip branch itself failed. Two reasons:

  1. `fetch("http://<ip>/", { headers: { host: name } })` follows the
     site's :80→:443 redirect into `https://<name>.<tld>/`, which isn't
     in ICANN DNS → "fetch failed".
  2. The site's cert is signed by a per-machine BNS root, not a public
     CA; standard TLS validation rejects it.

Both are fixed by connecting to the IP with SNI = name, pinning the
presented cert's SHA-256 against the on-chain `tls` record, and only
then issuing the HTTPS request over the same socket. The on-chain
fingerprint is the trust anchor BNS uses everywhere else (see
Argus/src/lib/ca.js).

Gateway (public-gateway.mjs): new pinnedHttpsGet + httpGet + ipRequest
helpers; case "ip" delegates. No silent HTTP fallback on pin failure
(a mismatch means "not the site the chain says it is").

Theseus (main.js): parallel port of the same helpers, Tor-aware
(routes through SocksProxyAgent when Tor is on). serveBns's inner
serveIp() delegates to ipRequest.

Verified live: `curl -sI https://navigate.st/bns/checkers.game.x/`
returns 200 OK with the checkers game (1,179,215 bytes, apex
`game.x` unchanged, served from Sia).
2026-08-16 20:28:58 +02:00
Local Dev
0888048ace Snapshot in-progress work: Ariadne mobile, Theseus password manager, Hephaestus
Several concurrent workstreams committed together as a checkpoint:

- Ariadne mobile resolver — BchFetcher/Bns/MainActivity resolution logic,
  AndroidManifest + build.ps1
- Theseus password manager — settings.html/chrome.html/settings-preload.js UI +
  main.js wiring + package.json resource; Argus password-vault.js, record-picker.js
  (+ tests) and resolver-web.d.ts
- Hephaestus — new BCH-wallet OIDC auth-proxy + Forgejo docker-compose and
  restic/S3 scripts (secrets referenced via env only; Hephaestus/.env is gitignored)
- Argus public-gateway.mjs updates
- Docs — root README, Email README/RUNBOOK, VPS access runbooks (Checkers/Deviant),
  site/hermes, WebsiteDev registry + faster-blocks, Failures/ AAAA-mangle writeup,
  Decentralized Storage map, coordination notes
- .gitignore — exclude /.keys/ and Hephaestus/.env
2026-08-14 23:17:18 +02:00
Local Dev
1d2faf64f3 Theseus: shield green polish, picker→Search section, toggle-vs-remove
Three follow-up asks from the previous ship:

1. Shield "secure" colour bumped from #4fd1a5 (mint) to #3fb950 — the
   GitHub-style saturated green, matches the +N/-N diff colour the user
   pointed at as reference.

2. Engine-picker "Search settings…" now opens the Search section
   directly instead of General. New IPC channel `focus-section` fires
   from main after picker-open-settings, carried through
   settings-preload as `onFocusSection`, and the settings.html sidebar
   handler exposes showSection(sec) so any section can be focused
   programmatically. Works for both a fresh settings tab (fires on
   did-finish-load) and an already-open one (fires immediately).

3. Toggle no longer removes an engine from the list. Two-tier state:
   INSTALLED (visible in the Settings list) and ENABLED (toggled on in
   the toolbar dropdown). Toggling off keeps the row visible with an
   .off class (dimmed 55%). Right-click any row → new context menu with
   "Remove from list" is what actually removes an engine (built-ins go
   back to the catalog, customs are dropped entirely).

Model changes:
- New settings.installedEngines persistent array (defaults to
  DEFAULT_ENABLED). enabledEngines becomes a subset of installedEngines.
- isInstalled(id) helper; allEngines() carries `installed: bool` alongside
  `enabled`.
- New IPC `remove-from-list` (right-click action); exposed as
  removeFromList in settings-preload.
- set-engine-enabled now also INSTALLS when enabling (the catalog "+ Add"
  flow), preserves installed state when disabling.
- add-engine (custom URL) auto-adds the new id to enabledEngines too.
- remove-engine (custom delete) prunes from enabledEngines as well.
- Never-empty invariant kept: enabledEngines falls back to ["duckduckgo"]
  if everything gets removed.

Settings UI:
- Enabled list shows all INSTALLED engines (was: only enabled), rendered
  with toggle reflecting enabled state; rows carry data-builtin so the
  context menu picks the right remove IPC.
- Catalog panel and Discover-more pane filter on !installed instead of
  !enabled — a toggled-off engine stays in the enabled list, not here.
- Ctxmenu is a floating .ctxmenu div; closes on outside click / Escape.
- .eng.off dims the row and mutes the name colour.

Preview harness stubs updated to include the `installed` field on every
engine + `removeFromList` and `onFocusSection` no-op stubs so
_settings-preview.html renders the new UI accurately.
2026-08-06 01:41:31 +02:00
Local Dev
ad1e5d4e1d Theseus: SameSite=None cookie shim for cross-site faucet embeds
Captcha-gated testnet faucets in the faucet hub set session cookies with
no SameSite attribute; Chromium defaults those to Lax and withholds them
inside cross-site iframes, so cookie-bound captcha endpoints 500
(tbch.googol.cash /captcha: 500 cookieless, 200 with the session cookie).
applyEmbedCookieShim() rewrites Set-Cookie on an allowlist of embed hosts
to append "; SameSite=None; Secure" so the cookie is frame-eligible.
Allowlist-scoped only — SameSite is CSRF protection, never relaxed globally.
2026-08-05 18:38:38 +02:00
Local Dev
65e9a18f33 Theseus: https:// scheme for BCDN + segmented-toggle registry switcher
BCDN pages now show https:// in the address bar (was bare 'host/', briefly bns://).
Rationale: BCNR replaces DNS (name resolution), NOT HTTP. Under the hood the
delivery IS HTTPS for s3/ip/p records; the on-chain h record has no transport
at all, but https:// is the least-surprising display. BCDN/ICANN badge is the
sole source-of-truth for which registry served us; scheme stays a convention.

Registry chip is now a proper segmented control:
- Two-chip toggle (collision candidates): single rounded pill, split in half
  with a divider — active side filled (acid green for BCDN, blue for ICANN),
  inactive side transparent + clickable. Feels like an on/off toggle.
- Single chip (pure ICANN or BCNR-unique TLD): standalone pill in the
  registry's colour. No toggle affordance since there's no alternative.

Same acid-glow-green (#d6ff3d) and blue (#4c9eff) as the collision.html prompt
so the whole registry palette is consistent across chrome + interstitial.
2026-08-02 19:26:47 +02:00
Local Dev
b5712cbcc7 Theseus: 'unsaved changes' confirmation on programmatic navigation
Adds a will-prevent-unload handler on each tab. When a page has a beforeunload
handler that returns non-null (typical for forms with unsaved input, in-browser
editors with a dirty document, etc.), Electron would silently cancel any
navigation attempt. Now we show a native two-button dialog — 'Stay on page' /
'Leave anyway' — matching how mainstream browsers behave.

Works for both user-initiated navigation (link clicks) AND our own programmatic
loads (address bar, chip switcher, in-tab collision prompt). Answers the
operator's ask: 'only if there is unsaved work that can be lost, should a
warning appear' (2026-08-02).
2026-08-02 18:50:58 +02:00
Local Dev
d1aa46c8d2 Theseus: switcher is direct-load (no more soft-mode re-prompt); blur URL on submit
Chip switcher now bypasses navigateTab/loadBns entirely and calls loadURL
directly. Rationale: the user explicitly clicked a registry chip; that IS the
choice. Routing through navigateTab -> loadBns was reaching the collision
decision path in some races and could re-show the soft-mode 'Open with...'
prompt as an unwanted extra step. Direct load guarantees the switch is atomic.

Prov is updated inline using entries.get(host) for the BCDN case (source/records/
category), same shape loadBns would have produced. internalNav flag guards the
programmatic load from the will-navigate handler (redirect chains stay clean).

Also: address bar blurs on Enter so the tabs-event handler can update it to
the actual loaded URL (search keyword no longer sticks in the address bar
after search results load).
2026-08-02 18:40:38 +02:00
Local Dev
c2ba26877f Theseus: download tracker, search split, discover-more tier, UX polish
Bundle of UX + feature work. Split from packaging by intent so the diff
is reviewable; the next Theseus rebuild ships it.

Features
- Download tracker (new): session.on("will-download") → per-item state
  {id, filename, url, mime, total, received, state, savePath, startedAt}
  with updated/done event handlers. New downloadsPop WebContentsView
  loads downloads.html (new file) + downloads-preload.js (new file);
  panel positioned under a new #downloads toolbar button between search
  and Tor. Full IPC: downloads-get, toggle/close/resize-downloads,
  download-open/show/cancel/clear, downloads-clear-all. In-memory only —
  cross-session persistence is a future addition. Button badge shows
  active count + spin/done/err color.

- Search engines split by kind + tier:
  * kind: "search" | "llm" — separate headers in picker + settings
    ("Search with" / "Ask an AI"). Empty sections hidden.
  * tier: "catalog" | "extra" — Settings now has THREE panes behind the
    "+ Add search engine" button: curated catalog, wider discoverable
    bank filtered by a live search input, custom URL form.
  * DEFAULT_ENABLED unchanged (5 major engines).
  * Custom user-added engines carry tier="custom" (never in catalog/extra
    panes).

- 9 tier="extra" engines added (all non-login ?q=): Marginalia, Stract,
  Yep, Presearch, MetaGer, Qwant, Swisscows, Naver, Baidu. Same rot rule
  as LLMs: if one starts bouncing to a login gate, drop it.

Bug fixes
- Loadbar collapses to 0px when idle (was reserving a permanent 2px
  strip below the address bar). .loadbar {height:0} + .loadbar.on
  {height:2px} + 120ms transition.
- Native <select> popup theme sync via :root { color-scheme: dark } +
  @media(prefers-color-scheme: light). nativeTheme.themeSource already
  drives prefers-color-scheme, so the OS popup color follows the app
  theme automatically (fixed light popup on dark app / vice versa).
- .ctl layout flipped to flex-direction: row with flex-wrap so
  anti-fingerprint mode + value fields fit side-by-side.

Settings restructure
- General section: Startup group at the top ("Open previous windows and
  tabs" toggle), then Appearance below with three visual THEME CARDS
  (System / Light / Dark) — small mock-browser previews per theme,
  Firefox-style, active card gets a blue ring. System pipes through to
  nativeTheme.themeSource = "system".
- Search promoted to a top-level sidebar item between General and
  Naming. Search-engine controls moved out of General into Search.
- Search section: enabled list shows only enabled engines, grouped by
  kind, drag-reorder within a kind. "+ Add search engine" opens the
  catalog/extras/custom-URL panel.

Search engine catalog trims (already flagged in prior work)
- Removed ChatGPT / Claude / You.com (login-gated ?q=).
- Removed SearXNG (federated; every single-instance default rots).

Docs
- TheseusNavigator/PENDING.md and GOTCHAS.md born with this work
  (see the HANDOFF.md commit for the convention).
- PENDING.md's own "session: 2026-08-02:theseus-ux-polish" group will be
  emptied after this ship lands.

Preview harness
- _preview.html + _settings-preview.html stubs updated with kind + tier
  + downloads seed + tier="extra" samples so the preview reflects reality.
  Both files are gitignored — local only.

Coordination
- Parallel session's collision-policy work (chrome.html registry chips,
  popover switcher, Naming section, in-tab collision prompt) already
  landed in commits 256079d/42b340f/b0d6375/78dddda. This commit adds
  cleanly on top.
2026-08-02 15:31:47 +02:00
Local Dev
c3f771ac1c Theseus: fix collision blank-page + broken-BCDN-open bugs
Two related bugs both caused by the tab's will-navigate handler racing with
programmatic loads:

Bug A (chip switch BCDN -> ICANN shows blank page, BCDN-priority mode):
  fallbackToWeb() calls webContents.loadURL('https://<host>/') to serve the
  ICANN version. That fired will-navigate, which saw a dotted host, ran
  isBnsHost() -> true, and RE-INVOKED navigateTab() recursively — but the
  transient='icann' override had already been consumed, so the recursive call
  fell back to bcnr-first, canceling the fallback mid-flight. Tab showed blank
  because both loads collided.
  Fix: mark programmatic loads with t.internalNav so will-navigate skips them.

Bug B ('Ask each time' -> Open BCDN doesn't load):
  Was going through a meta-refresh from bns://collision-choose/ to
  bns://<host>/?_collision=bcnr. The meta-refresh bypassed navigateTab, so the
  chrome/prov state was never updated (address bar, badges stayed stale).
  Fix: will-navigate now catches bns://collision-choose/ FIRST, applies the
  remember flag, sets t.collisionOverride, and routes via navigateTab so
  chrome + prov update correctly.
  Removed the serveBns collision-choose handler + the ?_collision URL-param
  path in loadBns (both dead now that will-navigate handles it).

Result:
- Soft-mode 'Open with...' -> Open BCDN loads the BCDN site cleanly.
- Chip switcher flips BCDN <-> ICANN with no blank flash, correct chrome.
2026-08-02 15:21:21 +02:00
Local Dev
bf54c245b7 Theseus: in-tab collision prompt + toggle-style switcher + auto-switch
Three UX fixes based on operator feedback (2026-08-02):

1) 'Open with…' is now a FULL-PAGE in-tab interstitial (was a modal window).
   - loadBns loads collision.html via loadFile with query params instead of
     opening a BrowserWindow.
   - collision.html is navigation-based: buttons redirect to a special
     bns://collision-choose/?host=…&choice=…&remember=…&resturl=… URL.
   - serveBns handles that URL: persists remember=name/tld choice, then returns
     a meta-refresh to the real target. For BCDN it appends ?_collision=bcnr
     one-shot marker; for ICANN it redirects to https://<host><path>.
   - loadBns strips and honors the ?_collision one-shot marker so BCDN
     redirects don't re-trigger the prompt.
   - Old modal path (collisionPromptOnce) removed.

2) Auto-switch from the address-bar chip: clicking BCDN/ICANN chip sets a
   transient per-tab collisionOverride that loadBns consumes ONCE for this
   navigation only, bypassing the soft-mode prompt entirely.

3) Address-bar toggle: chips stay visible during 'resolving' (no more
   flash-and-reappear); active chip is colored (acid green for BCDN, blue for
   ICANN — matching collision.html's palette); inactive is greyed with border,
   clickable to switch. Feels like a proper toggle.

Settings > Registries copy: operator's exact wording adopted (title stays
'Registries', body uses 'BCNR-unique' term for TLDs that only exist on BCNR).
2026-08-02 14:43:31 +02:00
Local Dev
75e468ae39 Theseus UX polish: BCDN/BCNR terminology + soft-mode prompt redesign
Terminology (user-facing labels now match the product/registry distinction):
- BCDN = Bitcoin Cash Domain Names (the product — what you view)
- BCNR = Bitcoin Cash Name Registry (the on-chain system that backs it)
Chrome badge, popover, Settings section and 'Open with…' prompt all show BCDN
for a resolved name. BCNR stays only where the registry itself is the subject.

'Open with…' prompt (collision.html):
- Larger window (640x520, was 480x340)
- Radio-select pattern with explicit Open / Cancel buttons (was auto-fire on click)
- Enter = Open, Esc = Cancel, keyboard-first
- Body copy per operator spec (no product-name-in-parens; blockchain-generic)
- Card titles kept short: 'BCNR / BCDN' + 'ICANN / IANA'

Address-bar chip:
- Says BCDN (not BCNR) for on-chain names; ICANN for web
- Dropped the '·.tld' suffix — TLD is already in the URL bar
- On collision candidates: shows BOTH chips (BCDN | ICANN) with active
  highlighted; clicking the inactive one flips the tab (via collision-switch)

Settings section renamed Naming → Registries. Copy rewritten in the same
BCDN/BCNR frame; policy labels are now 'BCDN first' / 'ICANN first' / 'Ask
each time'.

Passive 'also on BCNR' bar copy updated to BCDN.

Verified: all preload/settings/main/chrome JS + HTML parse; no secrets in
staging.
2026-08-02 13:02:47 +02:00
Local Dev
d0db4ac495 Collision modes (BCNR/ICANN) + root TLD cert + VPS electrum-source indexer
Theseus soft-mode UX: 'Open with...' modal on collision, per-name/per-TLD
overrides, live per-tab switcher in the site-info popover, and a Naming section
in Settings for policy + reset. Backed by an on-chain root TLD certificate
(tlds.bch) that resolver-web.js discovers via fetchBcnrTlds()/isBcnrNativeTld().

Companion pieces:
- Argus/src/indexer/ELECTRUM-SOURCE-README.md — the featherweight VPS variant
  (no BCHN node, no Fulcrum) now live as bns-indexer.service.
- Argus/DESIGN-root-tld-cert.md — clarified: NOT a governance workflow, just
  ordinary key management (single wallet MVP -> 2-of-3 multisig). List gates
  registration / surgical NRPT / soft-mode classifier — never resolution.
- ROADMAP-IDEAS.md — recorded SiaGit/GitHub.sia + user-friendly Sia UI ideas.

Full spec: Argus/DESIGN-collision-modes.md (already tracked).
2026-08-02 11:39:00 +02:00
Local Dev
73f3781825 Theseus: remove the non-functional "Add this site's search" (+) entry
Dropped the OpenSearch "+" item from the engine dropdown and the per-page
OpenSearch scan that fed it (it ran a fetch on every page load). Users add
engines via Settings instead.
2026-08-01 01:37:18 +02:00
Local Dev
48aa007591 Theseus: Firefox-style lock icon, LLM search engines, reliable favicons
- Address-bar security badge + popover lock redesigned to match Firefox's
  padlock (shackle + rounded body + keyhole).
- Added AI/LLM answer engines to the catalog: ChatGPT, Claude, Phind, You.com
  (Perplexity already present) — all accept a URL query. Off by default; enable
  in Settings.
- Favicons now load from DuckDuckGo's icon service (one privacy-respecting host,
  returns an icon for ANY domain) instead of guessing /favicon.ico per site,
  which failed for several engines and fell back to emoji. Verified loading.
2026-08-01 01:26:51 +02:00
Local Dev
e284864d3c Theseus: reorderable engine list + dropdown-or-type manual fingerprint fields
- Search engines can be reordered (▲/▼ per row); order persists in
  settings.engineOrder and drives the toolbar dropdown order.
- Manual anti-fingerprinting values are now dropdown-or-type (datalist):
  timezone (common IANA zones), language (common locales), and a city picker
  for location that fills exact lat/lon — each still accepts free typing.
2026-07-30 23:26:00 +02:00
Local Dev
69c2afca81 Theseus: media-device privacy fix + region/language pickers, AF under Privacy
- Privacy fix: enumerateDevices() leaked speaker (audiooutput) labels + device/
  group IDs even with camera/mic blocked. New "Hide media devices" (default on)
  blanks every device's label/deviceId/groupId and collapses to one per kind,
  matching Firefox — closes the WebRTC device-fingerprinting leak.
- Anti-fingerprinting moved into the Privacy section (own sidebar item removed).
- Location spoof: pick a world region (Europe/Asia/N&S America/Africa/Middle
  East/Australia) → representative coordinates, or Manual for exact lat/lon.
- Language spoof: pick from the top-10 world languages, or Manual for any locale.
2026-07-30 23:11:28 +02:00
Local Dev
0f823ba583 Theseus: Firefox-style search picker (favicons, catalog, scan)
- Custom engine dropdown as a floating overlay view (engine-picker.html) that
  renders REAL favicons per engine — a native <select> can't show images.
- Larger catalog (DuckDuckGo/Google/Brave/Bing/Startpage/Yandex/Ecosia/Mojeek/
  SearXNG/Wikipedia/Perplexity); Settings has a favicon checklist to choose
  which appear in the dropdown (enabledEngines), plus manual add/remove.
- OpenSearch "scan": pages advertising a search engine surface an
  "Add <site>'s search" entry in the dropdown.
- Toolbar search box now just a magnifier button that opens the dropdown
  (no per-engine icon in the bar).
2026-07-30 22:55:52 +02:00
Local Dev
ad34ad3bbe Theseus: per-engine symbols + light-theme cascade fix
- Each search engine shows a symbol in the toolbar dropdown, the placeholder,
  and Settings (🦆 DuckDuckGo, 🦁 Brave, 🔵 Google, 🔎 Bing, 🛡️ Startpage,
  🔴 Yandex); custom engines take an optional symbol (defaults to 🔍).
- Fix: light-theme @media blocks for settings/popover/home were placed before
  the base rules and lost the cascade (sidebar stayed dark) — moved them last.
2026-07-30 22:09:26 +02:00
Local Dev
2757cd8c64 Theseus: light/dark theme, redesigned search box, trimmed engines, WebRTC modes
- Light/dark/system theme (Settings > General) via nativeTheme.themeSource
  driving prefers-color-scheme across chrome, settings, popover, and home.
- Search box redesigned: a magnifier icon opens the engine dropdown, the rest
  is a wider typing field; styled to match the browser (was a raw <select>).
- Trimmed built-in engines to DuckDuckGo/Google/Brave/Bing/Startpage/Yandex;
  custom engines still add/remove via "Add / edit engines…".
- WebRTC: the "WebRTC Network Limiter" extension can't run in Electron
  (chrome.privacy API is unavailable), so its function is now a native
  4-mode WebRTC IP policy in Settings > Privacy.
- Address-bar placeholder: "Ask a search engine or enter web address".
2026-07-30 22:00:01 +02:00
Local Dev
01429990ea Theseus: Firefox-style security panel + search-engine picker
- Security popover redesigned to resemble Firefox's site-info panel:
  lock/shield hero, "Connection secure" status, host, plain-language
  subtitle, and a details grid; the overlay view auto-sizes to content.
- Search-engine picker returned to the toolbar with an "Add / edit engines…"
  entry that opens Settings; added Ecosia, Mojeek, Presearch.
- Custom search engines: add (name + URL template with %s) / remove in
  Settings > General; used by address-bar search.
- Address-bar placeholder: "Ask a search engine or enter web address".
2026-07-30 20:58:45 +02:00
Local Dev
b14aebeb85 Theseus: perf + loading UI, minimal chrome, sidebar settings
- Performance: cache the chain index (was rebuilt on every navigation) and
  warm it at startup — .bch pages open near-instantly after the first.
- Loading indicator: indeterminate bar under the toolbar, per-tab spinner,
  and reload⇄stop button (driven by did-start/stop-loading + BNS resolve).
- Chrome cleanup: removed the bottom status bar and the redundant search
  box; security padlock now always present at the front of the address bar;
  minimal ICANN/BCNR pill at the end; favorites bar shows only on new-tab.
- Search: added Yandex; engine picker moved into Settings > General.
- Settings redesigned with a left sidebar (General / Performance / Privacy /
  Anti-fingerprinting). Anti-fingerprinting now Show/Hide/Spoof/Manual for
  timezone, language, AND location (geolocation coords overridden in-page).
2026-07-30 19:29:32 +02:00
Local Dev
89c80a0103 Theseus navigator UI overhaul, BNS-only indexer, site restructure
Theseus Navigator:
- Firefox-style toolbar: SVG back/fwd/reload/home buttons
- security padlock badge in the address bar opens a floating site-info
  overlay VIEW (on top of the page, never pushes content down)
- Edge-style favorites bar (shown only on the new-tab/home page)
- search-engine picker (DuckDuckGo/Google/Bing/Brave/Startpage)
- Theseus logo moved right, opens Settings; native menu removed
- address bar doubles as search; bottom resolver status line

Argus:
- BNS-only indexer: an electrum endpoint over a bare BCHN node
  (watches just the beacon, caches txs so no txindex needed) + test
- registry lifecycle / portfolio / name-index modules

Site restructure (nav/apps/store/hermes/choose) + runbook notes.
2026-07-30 08:16:55 +02:00
Local Dev
7898e78ff4 Initial commit — Silent Mode baseline (2026-07-29)
Snapshot of the decentralized-web stack at the point of the resolver+Theseus
rebuild deploy. Includes:

- Argus (BNS engine + resolver daemon + Sia gateway)
- AriadneResolver (Windows Inno installer bundle + Android APK sources +
  Firefox extension)
- TheseusNavigator (Electron browser)
- site/ (silentmode.st content, deployed to Sia at bns/silentmode/)
- design docs, roadmap, protocol spec

Secrets excluded via .gitignore: Argus/sia-s3.json, Argus/wallets.json,
Argus/ca/*.key,*.crt. Build outputs, node_modules, and bundled runtimes
also excluded.

Shipped hashes on dl.silentmode.st at this commit:
  AriadneResolver-Setup-0.1.0.exe    5bcb216eef31ea28ed767e4134ab74bd5ac69dfbd365fd249e9e6938e55c986a
  TheseusNavigator-Setup-0.0.1.exe   7c735e88bad2da3347145adba3016c8f626a18b8422289c8c6ba471972e2952b
  TheseusNavigator-0.0.1-portable.exe 008fd84445babeabb401b2bca40ea9466b24b0e6d6c85104da7640c5c5c84521
  ariadne-v0.2.apk                   635c8f04d44ef855a8390b9eeddb8cd2d50622e81cc4e5004e8daffc1bb0425c
2026-07-29 13:54:34 +02:00