Commit graph

115 commits

Author SHA1 Message Date
Local Dev
fb3fa84d15 Theseus 0.3.123 (cont'd): catalog visible by default, "+ Show more" at the bottom
The curated catalog was hidden behind the "+ Add search engine" button at
the top-right of the header, which buried the common case (pick one of
the five curated options) behind an extra click. Flipped the layout so
the curated catalog paints inline right below the Enabled list, and the
"+ Show more" button lives at the bottom — opens the Discover-more bank
and the custom-URL form. Same change on the AI engines page.
2026-10-27 20:58:48 +02:00
Local Dev
aea158ab05 Theseus 0.3.122 followups: AI engines page, Pick-AI edit button, Pithos panel, Did-you-mean on DNS failures, LLM picker UI polish, real-brand icon preference
Shipped on top of the 0.3.122 Settings restructure commit.

AI engines settings page. New top-level nav entry "AI engines" with its own
Default picker, Enabled list, catalog, discover filter and + Add AI engine
form. The Search page is now LLM-free — ENGINE_KINDS is `["search"]`, the
Search catalog filters out kind="llm" entries, and the Search custom-URL
form only adds kind="search" engines. add-engine IPC now accepts a kind:
"llm" field (sets defaultLlmId instead of searchEngine for LLM customs).

Pick-AI picker. Borders removed, corners rounded (16px), heavier shadow;
"Pick AI engines…" footer row dropped. Edit button (pencil icon) added to
the header; opens the new AI engines settings page via `llm-picker-settings`,
which now routes to the "ai-engines" slug (openSettingsTab falls through to
"search" for legacy profiles that lack the section).

Icon priority flipped. allEngines() now prefers a fetched favicon (the
site's own brand mark, cached per profile) over the bundled generic tile.
Bundled tiles still carry the first paint while the fetch runs, and the
emit after fetch swaps in the real brand. Offline profiles see the tile
indefinitely — same reliability as before.

Pithos sidebar panel. bundled-addons/pithos/addon.json declares a `panels`
block so sidebarLeftPanels() returns Pithos's panel BEFORE the add-on's
on-demand activation. Previously, the Sidebar settings' Theseus native
apps card was missing Pithos: it only appeared after first launch, when
the add-on's own registerSidebarPanel call ran. Hermes already did this.

Did-you-mean on DNS failures. The error page's "Did you mean" row now
merges BCNR warm-index matches with ICANN alternative-TLD swaps + history
base-name matches. For my.mynymbox.com the suggestions are my.mynymbox.io,
mynymbox.io, my.mynymbox.net, mynymbox.net — the subdomain-strip is
interleaved so a mistake like typing my.mynymbox.com when the real site
is client.mynymbox.io still surfaces mynymbox.io near the top. Both paths
are local-only; no network, no leak of what the user is browsing.

Sidebar settings tightened. "How panels open" collapsed to one row with
two side-by-side option cards; "Reset panel widths" and "AI engine in the
sidebar" are one-liners now.
2026-10-27 20:50:40 +02:00
Local Dev
1a31ac0393 Theseus 0.3.122: Settings restructure — General first-rows, top-level Ariadne & Aegis, Sidebar native-apps card
Settings page UX restructure.

- Nav bar: reordered to General, Language, Search, Passwords, Performance,
  Privacy, Names & Certificates, Plug-ins, Ariadne's Thread, Aegis,
  Extensions, Sidebar, Apps. Dropped the top-level Theseus ID entry;
  Theseus ID is reachable from General now. Added two new top-level
  entries — Ariadne's Thread and Aegis — each a small landing section
  that chevrons into the existing plug-in sub-page (Ariadne) or opens the
  wallet panel via openPanel("aegis:main") (Aegis).

- General section: first four rows are now Make-Theseus-default, Theseus
  Shield (chevron + on/off toggle), Startup (restore tabs), Updates
  (version + Check button), followed by a Theseus ID chevron row.
  Appearance / Developer tools / Registries / About stay below in the
  same order. Removed the "Default handling" sub-head; the default-
  browser and file-associations rows moved to the top. The Shield row
  uses listAddons() + setAddonEnabled("blocker", on) for state; the
  chevron falls back to openPanel("blocker:main").

- Names & Certificates: dropped the always-on BNS row at the top.

- Plug-ins main page: Ariadne's Thread and Aegis rows removed — they
  moved to top-level. The page now links into the new landing sections.
  Hidden placeholders keep the DOM ids (ariadneStatusMain, aegisCheckMain
  …) so the shared refresh code keeps working. Sub-pages (plugins-
  ariadne, plugins-aegis) untouched.

- Extensions list: Hermes and Pithos hidden from the list (they live in
  the Sidebar card now). docx-editor and pdf-editor sort last within
  each group so smaller utilities stay near the top.

- Toolbar add-on strip (chrome.html): same last-sort rule for docx-editor
  and pdf-editor, applied to the rank() ordering so unknown keys without
  a saved dockOrder keep registration order and the editors land past
  them.

- Sidebar section: added two cards at the top. "Theseus native apps"
  (Hermes + Pithos) and "Third-party apps" (the sidebar quick-links /
  Web apps). Native apps appear first. The Extension panels section
  still lists everything else that opens from the strip; Hermes and
  Pithos are filtered out of it so they don't appear twice.

- Version bumped to 0.3.122. No new IPCs — reuses listAddons,
  setAddonEnabled, openPanel, sidebarLeftPanels, addonInvoke.
2026-10-27 18:04:49 +02:00
Local Dev
41eb0acec9 Theseus 0.3.121: default-browser toggle + "Open with Theseus" in file menus
New Settings › General › Default handling section (Windows only, hidden on
other platforms until there is a backing path for them). Two toggles:

  * "Make Theseus the default browser" registers Theseus for http and https
    via app.setAsDefaultProtocolClient and lands the user on Windows
    Settings › Default apps so they can confirm the switch — Windows does
    not let an app assert itself as default, so we do what we can and point
    them at the UI that owns the final decision.
  * "Show Open with Theseus in file menus" writes HKCU\Software\Classes
    entries for .html, .htm, .pdf, .svg and .xhtml so Theseus appears in
    the Open-with dropdown for those files. Everything lands under HKCU
    (no admin). A matching RegisteredApplications\Theseus Navigator entry
    makes Theseus visible by name in Settings › Default apps.

Also handles file paths and URL arguments on the command line — this is
what Windows passes when the user picks Theseus from an Open-with menu, or
clicks a link in another app once Theseus is the default. argv[0] is the
exe; the first entry that is a recognised scheme (http/https/file/bns) or
an existing local file wins and opens as a new tab. Second-instance was
already wired to handleLaunch via requestSingleInstanceLock, so a running
Theseus just picks up the file.
2026-10-27 17:28:32 +02:00
Local Dev
9095a5ecf9 Theseus 0.3.112: AI picker chevron fixed, home polish, performance toggles
Three home-page tweaks:
- Removed the YOUR LINKS and SILENT MODE section headings; the two
  panels now stand on their own without labels.
- The + Add card cell now matches the real cards' size and shape so
  it reads as a peer of the grid rather than a short dashed strip.
- Empty slot cells in the left grid are invisible by default and
  only reveal their dashed outline when you hover the strip or drag
  a link over them. The + add-link cell stays visible as the
  discoverable entry.

AI engine picker:
- The chevron badge on the AI button is now a plain span with the
  button role, not a nested <button> inside the main AI button.
  Chromium flattened the invalid nesting so the chevron's own click
  handler never fired; the picker now opens from the chevron and
  from keyboard Enter / Space as well as right-click.

Performance (Settings > Performance > Startup):
- Pithos ships with activation: on-demand; its control server for
  s3d no longer boots at launch. Wakes on first panel open or a
  pithos.sia navigation.
- Hermes relay auto-restore (1.5 s after launch) is now gated by a
  new setting hermesAutoRestore (default on). Turn it off to skip
  background Nostr relay connections for users who don't live in
  Hermes.
- New setting pithosAtLaunch (default off) mirrors the existing
  walletAtLaunch so users who do want Pithos ready at launch can
  opt back in.
- The Lightest preset now also turns off pithosAtLaunch and
  hermesAutoRestore.
2026-10-24 14:20:20 +03:00
Dev
c06557e957 Theseus: Claude connector, an opt-in MCP server for the signed-in profile
Claude sessions could not work inside the user's own profile: the desktop
app's browser lost synthetic pastes and typing in Medium's editor, and a
browser-wide --remote-debugging-port would hand every session to any local
process. The connector is a built-in extension, off until turned on in
Settings > Extensions, that serves MCP on 127.0.0.1 behind a per-install
token and drives one tab at a time through that tab's webContents.debugger.

- Host: a "tab-control" capability (built-in add-ons only) for listing,
  opening, navigating, closing and focusing tabs, handing out web tabs'
  webContents (never Settings, extension or home tabs), and a toolbar chip
  plus tab outline while a client is connected; the chip's X stops it.
  Built-in add-ons can ship "defaultEnabled": false.
- Tools: list_tabs, open_tab, navigate, activate_tab, close_tab, screenshot,
  read_page_text, snapshot (accessibility tree with refs), click, type
  (insertText + real Enter), press_key, paste_html / paste_text (system
  clipboard + webContents.paste, clipboard restored), upload_file
  (DOM.setFileInputFiles), wait_for, and evaluate behind its own switch.
- Safety: first use of each site asks the user; password and one-time-code
  fields are refused; uploads only from folders the user added; requests
  from web pages or foreign Host headers are refused; every action is
  logged with a timestamp in the panel and actions.log.
- Claude Desktop runs stdio-shim.cjs through the Theseus executable in Node
  mode.
- Tests: unit (protocol, auth, policy, shim) and an end-to-end run on a
  throwaway profile that types, pastes HTML with a heading and a link, and
  uploads a PNG on a local page.
2026-10-23 17:45:02 +03:00
Local Dev
22e2b3f75e Theseus 0.3.100: LLM sidebar button, Hermes consolidation, Settings polish
The gear popover at the bottom of the left strip is reduced to a single
"Open sidebar settings" item; the overlay/push radio and "Reset panel
widths" controls move to Settings › Sidebar permanently, so there's one
source of truth for the sidebar's own options.

A new sparkle button is added below the icon stack: it opens the user's
default AI engine (any search engine with kind=llm — Perplexity ships
on by default now; Phind is in the catalog). Click opens the engine in
the left panel like the existing web-app quick-links; right-click (or
the small chevron in the button's corner) pops a native menu of every
enabled LLM, each one-click to switch default + open. The button hides
itself when no LLM engine is enabled.

Hermes consolidation. The hermes.x web quick-link added in 0.3.99 is
retracted — the bundled Hermes extension already registers a left-strip
panel (side: "left"), so the web link was duplicating it. Any profile
that caught 0.3.99 gets the stray entry yanked on first load.
Ctrl+Shift+E now opens the Hermes panel in the active window's left
sidebar instead of the standalone Messages BrowserWindow; the window
is still reachable via the ⧉ pop-out button in the panel header for
users who want a free-floating client. The home-page Silent Mode card
for Hermes.X comes back (it's a link to the web page, not a shortcut
to the panel — different purpose).
2026-10-21 20:10:11 +03:00
Local Dev
208a192fc9 Theseus 0.3.99: SNS + Unstoppable adapters, Hermes moves to the sidebar
Two more resolvers under Settings › Names & Certificates. SNS — .sol names
resolve through Bonfida's public SDK proxy, preferring an explicit URL
record over an IPFS record (served via a configurable IPFS gateway).
Unstoppable Domains — the full UD TLD roster (.crypto, .nft, .wallet,
.bitcoin, .blockchain, .dao, …) through resolve.unstoppabledomains.com;
browser.redirect_url takes precedence over an ipfs record. .x stays with
BNS — the UD adapter explicitly skips it so a Sirius registration can
never be shadowed by an Unstoppable one.

Both proxies are Cloudflare-fronted; users on blocked IPs can swap the
endpoint in Advanced. The handshake adapter's skip-list grows to cover
.sol and every UD TLD so a user with HNS + UD + SNS all enabled gets the
specific adapter, never the generic hns.to gateway, for those names.

Hermes.X moves from the home page's Silent Mode card grid to the Theseus
left-sidebar quick-link strip, slotting in right after Proton Mail. The
default list grows to 6 entries; existing profiles pick it up through the
LATER_DEFAULTS one-time offer (same mechanism Proton used in 0.3.88).
Users who still have Hermes on their custom home page can remove it manually.
2026-10-21 19:12:19 +03:00
Local Dev
227d013013 Theseus 0.3.98: Namecoin + Handshake adapters
Two more adapters under Settings › Names & Certificates:

Namecoin — .bit names resolve by calling name_show "d/<name>" on the
configured JSON-RPC endpoint and following `translate` or `alias` records
to a clearnet host. Pure IP records aren't followed yet (that needs
in-browser DNS override + TLS pinning, which comes later). Namecoin has
no free public RPC, so the toggle is off by default and the row carries a
"Needs an RPC URL" note instead of a "Not wired yet" one; set yours in
Advanced.

Handshake — non-ICANN hostnames resolve through the public hns.to web
gateway (user-overridable in Advanced). hns.to proxies the request to a
Handshake node and serves the on-chain content. A bundled list of common
ICANN TLDs (original gTLDs + popular new gTLDs + every ccTLD) keeps the
default collision policy ("icann-wins") from hijacking clearnet names.
Setting the policy to "hns-wins" removes that gate.

Both adapters follow the ENS adapter's shape — tryExtraResolvers in main.js
calls each in turn after the BNS lookup; the first one that returns a URL
loads it. Settings copy on both rows updated.
2026-10-21 02:40:33 +03:00
Local Dev
3985772ba3 Theseus 0.3.97: ENS adapter — .eth names resolve to IPFS/IPNS
First real resolver behind the Names & Certificates toggles. When the ENS
switch is on, typing a .eth name in the address bar runs:
  1. namehash(name)                  (EIP-137 keccak256 recursion)
  2. eth_call  resolver(bytes32)     on the ENS registry
  3. eth_call  contenthash(bytes32)  on the returned resolver address
  4. decode EIP-1577 (varint protocol + CID) → ipfs|ipns|swarm
  5. navigate to <gateway>/ipfs/<cid> (or /ipns/<cid>)

The adapter lives in lib/resolvers/ens.cjs; main.js's navigateTab calls it
through tryExtraResolvers after the BNS check and before the clearnet
fallback. Defaults: publicnode's free Ethereum RPC (Cloudflare's eth-RPC
returns "Internal error" for ENS calls in testing) and ipfs.io as the
gateway (Cloudflare retired their IPFS gateway in 2024). Both are
user-overridable in Settings › Names & Certificates › Advanced.

CIDv0 (Qm… base58) and CIDv1 (bafy… base32) both encode; swarm names
cache a "not yet supported" negative result for one minute until we add a
swarm gateway option. Positive answers cache for 10 min, negatives for 1 min.
2026-10-21 02:20:23 +03:00
Local Dev
5d34fa18af Theseus 0.3.95: Names & Certificates settings — UI shell for extra resolvers
New top-level settings page lets the user pre-configure which non-BNS name
systems Theseus will resolve (ENS, Namecoin, Handshake, SNS, Unstoppable)
and which extra TLS roots it will trust (Russian National CA, scoped by TLD).
BNS is always on; everything else is off by default. Each resolver row has
its own Advanced disclosure for RPC URL, gateway, endpoint and collision
policy. The Russian CA row's Advanced holds the comma-separated TLD scope
list that limits what the CA can sign for (default .ru/.рф/.gov.ru/.online.ru).

State persists to %APPDATA%\Theseus\extra-resolvers.json via settings-only
IPC (extra-resolvers-get / extra-resolvers-set / extra-resolvers-reset,
origin-gated to the settings page). The adapters and setCertificateVerifyProc
hook that read this file land in 0.3.96 (ENS) → 0.3.99 (Russian CA); each
row's "Not wired yet — coming in 0.3.9X" caption goes away as its adapter
ships. No home.html change in this release.
2026-10-21 01:19:06 +03:00
Local Dev
f6d71bd48f Theseus: switch sidebar apps and extension panels on and off
Settings › Sidebar had a Hide button for extension panels and only Remove
for web apps. Every row now has an on/off switch: off takes the app off
the strip and closes its panel, but keeps it in the list with its sign-in
and width. Web apps and panels share the store (leftPanelsHidden), and a
link that would open in a switched-off app's panel opens in a tab.
2026-10-08 01:43:03 +02:00
Local Dev
1ad2b5d647 Theseus: Settings › Sidebar shows the apps' icons and suggests well-known ones
The web app rows in Settings showed a letter where the strip shows the
app's icon. The icons now live in strip-icons.js, shared by both. Typing
the start of a well-known app's name or address in the add row shows the
rest in grey, the address field offers that app's address, and the right
arrow takes it.
2026-10-08 01:38:53 +02:00
Local Dev
4e41179d38 Theseus: sidebar and installed apps stay signed in across restarts
"Clear site storage on quit" wiped the IndexedDB and localStorage where
WhatsApp Web and Telegram Web keep their sessions, so both signed out at
every launch; only sites with a login in the vault were spared. The keep
list now also holds the sidebar web apps and the installed apps. The
switch sits under both clearing switches it applies to (it read as a
cookies-only option, indented under that one) and says what it covers.
2026-10-07 23:52:15 +02:00
Local Dev
110d0157de Theseus: sidebar panels open over the page or push it aside
An open sidebar panel (a web app or an extension panel) now has a bar on
top with its name, Maximize/Restore (it covers the page, up to the right
sidebar) and Close. How panels sit next to the page is a choice, in the
new gear above + on the strip and in Settings > Sidebar: over the page,
where it floats on the left of a page that keeps its width and a click in
the page closes it; or pushing the page aside, where it stays open until
closed. That choice replaces the pin, which only existed to keep a panel
open on a page click.

Also: a window laid out while minimized reads a zero-size area, so every
view was left at 0x0 and the window came back blank until resized.
Layout now skips a minimized window and runs again on restore.
2026-10-07 23:28:01 +02:00
Local Dev
c4b3e77238 PIN pads: an Unlock button, Next to set 6-8 digits, bigger keys
A PIN can be 6, 7 or 8 digits, but the pads submitted on the last expected
digit, and setting one meant first picking its length. Now:
- The unlock prompt fills the dots for the PIN's length and sends it with
  Unlock (or Enter) beside Cancel, so a slip can still be fixed with the
  delete key first.
- Setting a PIN takes 6 to 8 digits with Next (the last two dots are marked
  optional), and the repeat step saves with Save PIN once it has as many.
- Keys are larger (76x56, 24px digits) and the dots a little bigger.
2026-10-05 20:59:25 +02:00
Local Dev
b4362f2e54 PIN pads: calm hover and press states
Moving the pointer across the keypad lit each key it crossed with a bright
acid border and digit, and every filled dot glowed, which read as the pad
flashing while a PIN was typed. Keys now only lighten slightly on hover and
press in a little when clicked; dots fill without the glow.
2026-10-05 20:28:20 +02:00
Local Dev
869b94a85a Merge release/0.3.80: Theseus ID, guided PIN, save-login offers 2026-10-05 20:15:33 +02:00
Local Dev
e598fdf1e6 Theseus Settings: the menu runs full height again
The Apps list styled its rows with .app, the class the Settings page itself
uses for its layout. The later rule won on the page too: align-items:center
pulled the left menu into a short box in the middle of the window, with a
border, padding and rounded corners it never had. The rows are .webapp now.
2026-10-05 20:14:16 +02:00
Local Dev
8db0a2f9f9 Merge branch 'claude/focused-visvesvaraya-177d2e' into release/0.3.80
# Conflicts:
#	TheseusNavigator/main.js
#	TheseusNavigator/settings.html
2026-10-05 20:04:12 +02:00
Local Dev
19dc0649dd Theseus Settings: a Sidebar section for the left strip
Quick links sat in a small block under General, and the panel behaviour
added this week (per-app widths, pins, the Opera-style auto-hide) had no
place in Settings at all. Settings › Sidebar now holds the strip switch,
the web apps (drag to reorder like Search engines, pin, reset a remembered
width, remove), the extension panels on the strip (show or hide each one,
pin, reset width) and three switches for when the open panel steps aside:
a new tab, the right side panel opening, a click in the page. Apps gains
"Add to sidebar", so an installed site can also open in the strip.
2026-10-04 21:53:39 +02:00
Local Dev
b8529f4a7f PIN pads: stop the keypad jumping on every press
The vault prompt rebuilt its whole dialog on each digit, which replayed the
pop-in animation, and because the dialog is centred, every change in the
message line's height moved the whole pad under the user's finger. Presses
and answers now update the dots and message in place, the message lines
keep a fixed two-line height, and switching between PIN and password clears
a half-typed PIN. The Settings PIN dialog keeps the 6/7/8 row's space on the
repeat step so the pad stays put between steps. Measured on a scratch
profile: key positions identical across presses, errors and steps.
2026-10-04 21:18:26 +02:00
Local Dev
3243add70e Theseus ID in Theseus: window.theseusId.signIn and Settings › Theseus ID
Pages of Silent Mode projects can now sign the user in with their Theseus
ID instead of a wallet phrase typed into the page. Theseus writes the
sign-in message itself, takes the origin from the committed top frame, and
signs as a project only on an origin that project's list includes, so a
phishing page cannot get another project's signature and no page can use
the ID key to sign anything else.

- lib/theseus-id.cjs: the policy (first sign-in always asks and lets the
  user pick a private or One ID; Silent Mode projects are silent after
  that while the vault is open; per-site "always"; 10 silent signatures per
  minute per origin), the per-project record encrypted under a key derived
  from the vault, origin-list fetching with a 1 h cache and a 7-day stale
  fallback, and ID moves that send a proof signed by both keys and only
  finish once the project confirms.
- A locked vault is unlocked only for a page the user just clicked or typed
  in: navigator.userActivation alone is true on load for pages opened with
  loadURL, which would let a page pop the vault prompt by itself.
- Settings › Theseus ID: default mode, One ID, automatic sign-in toggle,
  signed-in projects (always, change ID, new ID, revoke) and a recovery key
  behind a fresh PIN / password check.
- TheseusID/registry/projects.json is the first-party list (Hephaestus,
  Sirius, Pithos); it and TheseusID/lib ship as extraResources.
- Token-aware cashaddrs (BNS owners) now decode for owner-signed lists.

Verified on a scratch profile against a local test project whose server
checks signatures with TheseusID/lib/verify.mjs: locked vault on load gives
"locked" with no prompt, first sign-in prompt, silent second sign-in, a
claimed foreign project refused without a prompt, an ID move that keeps the
project's account, and the recovery key behind the confirm prompt.
2026-10-04 20:48:07 +02:00
Local Dev
a6f7b335a5 Vault: PIN setup steps, 6-8 digit PINs, save and offer logins, keep sign-ins
The PIN could only be six digits and was set from three bare inputs; the
unlock prompt sat at the top of the page; and the password manager only
filled when you found the key chip, never offered to save, and "Clear
cookies on quit" signed you out of every site, including the ones whose
login the vault already holds.

- PINs are 6 to 8 digits. The PIN record stores its length so pads draw the
  right number of dots and submit on the last digit; a PIN of the wrong
  length is refused without a strike, so an older Aegis pad cannot burn the
  count against an 8-digit PIN.
- Settings sets a PIN in steps: master password, choose the PIN on a pad
  (6/7/8), repeat it, done. The locked vault opens Theseus's own prompt,
  which is now centred, with the PIN pad or the master password field.
- After a sign-in or sign-up form is sent and the page moves on, Theseus
  offers to save (or update) the login, with an optional "ask for my PIN or
  password before filling it". Focusing a login form offers the saved
  logins under it; on a locked vault it offers to unlock first. A failed
  login (the password field still showing) gets no offer.
- "Keep sign-ins for sites in your vault" (on): the quit clear spares the
  cookies and site storage of sites with a saved login. Their hostnames are
  kept sealed with the OS keystore so the list is readable at quit while
  the vault is locked. Verified end to end on a scratch profile: signed in,
  restarted, still signed in; another site's cookie was cleared.
2026-10-04 20:23:43 +02:00
Local Dev
3dbbc452cd fix(theseus): an installed app can be closed and removed; Settings lists apps
An app whose page has a beforeunload handler (CoinSpectrum) could not be
closed: tabs answer the page's "stay?" request, app windows had nothing
listening, and Electron reads silence as a veto. Closing the window is now
always the user's call; a reload or navigation inside the app asks, as a
tab does.

Removing it looked dead for a related reason. The confirmation was drawn
as Theseus's sheet in the main window, where the user was not looking (or
nowhere, with the main window closed), and the removal then closed the app
window with the same call the page vetoes. The question is now asked on
the app window that asked, and removal destroys the window.

Installed apps were only reachable from the address-bar chip while on the
site. Settings gets an Apps page, at theseus://settings/apps, that lists
them with Open and Remove and follows installs, removals and open windows.
The three list calls it uses are settings-only now.
2026-10-04 19:15:11 +02:00
Local Dev
e3729e7470 Merge charming-mclean: a legacy Middle East location no longer kills Settings 2026-10-04 17:38:44 +02:00
Local Dev
828100e2ce Theseus: warn before opening a name a blocklist flags
The blocklist consumer existed in the resolver library and the gateway, but
the browser opened a flagged name without comment. Now the indexer process
reads the subscribed lists from the chain every ten minutes and hands the
flags to main. A flagged name loads a warning page naming the reason, the
list and the report; the user may continue, and that is remembered per name.

Two gates, because content is reached two ways. loadBns shows the real
interstitial. serveBns refuses with an inline page on every path that skips
it: reload, back and forward, bns:// links, web app windows. The inline page
has no button, since a page at the site's own origin must not be able to
approve itself; only blocked.html may ask to continue, checked by file URL.

Settings › Naming has the policy: warn (default), never open, or ignore the
lists. The csam reason is never offered a way through. A list that cannot be
read keeps the last known flags and never stops a name from resolving.

The gateway put its own warning in front of flagged sites, which this
browser could not get past: it fetches files itself, with no cookie jar. It
now sends x-bns-policy: client and the gateway stays out of the way for a
client that says it decides for itself.

dev/blocklist-selftest.js runs the real protocol handler and decision
functions against a scratch profile.
2026-10-04 15:50:35 +02:00
Silent Mode
0ce2ab95dd Theseus: translator provider setting — LibreTranslate or Bergamot
A new row in Settings › Language, Translator engine: a radio between the
hosted LibreTranslate backend (the current default) and the on-device
Bergamot engine. The chip and auto-translate route through a single
dispatcher (translatorCall) that picks the provider by setting; adding
or removing an engine only touches the dispatcher.

Bergamot's WASM runtime and model manager are not bundled yet, so the
Bergamot provider falls back to LibreTranslate for now and prints a
one-time console notice. The setting itself is real today — a user can
declare their preference and the real engine lands in a later release
without the user touching Settings again. See bergamot.x for status.
2026-10-04 14:20:18 +02:00
Local Dev
b99d3a6e54 Merge branch 'master' into claude/goofy-dubinsky-1f8f33 2026-10-04 04:32:44 +02:00
Local Dev
9d6d8c3cc5 Theseus: one PIN — the vault PIN, offered to Aegis through api.vault.pin
Theseus and Aegis each wrapped the same master password under their
own PIN: two offline targets, two guess budgets, and two PINs to keep
in step. The vault PIN is now the only one. Built-in add-ons get
api.vault.pin {status, unlock, set, clear} (advertised by
features.vaultPin); unlock(pin) opens the vault in main and answers
only { ok } or why not, so the master password stays in main.

The policy is the one Aegis's PIN screens describe: five wrong PINs
lock the PIN for 15 minutes, every further wrong one locks it again,
and the master password always works. The unlock prompt uses the same
PIN pad and the same wording as Aegis, and Settings says so.
2026-10-04 04:15:15 +02:00
Local Dev
b9ffe7e1a0 Theseus Settings: the vault notes describe the vault that exists
Privacy still said there is no persistent password manager and pointed to
Bitwarden or KeePass, and Passwords said autofill was "phase 2". Both
contradicted the encrypted vault and the address-bar fill button Theseus
has had for a while.
2026-10-04 04:10:50 +02:00
Local Dev
561cb122ea Vault PIN: tie it to the TPM and never store it unsealed
Theseus's own quick-unlock PIN had the same limit as Aegis's: once the
DPAPI seal is opened (as the user, or from a disk image plus the
Windows password) the 6-digit PIN falls to an offline search. The PIN
is now also the authorization value of a Platform Crypto Provider TPM
key whose secret is mixed into the wrapping key, so the chip's lockout
bounds guessing; lib/tpm-pin.cjs is the same module Aegis uses.

set() now refuses when there is no real OS keystore (Linux basic_text
included) instead of writing the blob in the clear, an unsealed record
from an older build is deleted, and Settings says what the PIN actually
protects against on this machine.
2026-10-04 03:42:02 +02:00
Local Dev
41523a5cbf Settings: a legacy "Middle East" location no longer kills the page
Profiles from before the country picker carry locationMode "spoof" with a
region; the Settings page migrates them to a country on load. middle_east
mapped to AE, which was missing from COUNTRIES, so the destructure threw
partway through the page script. Everything wired after that point never
ran: the Ariadne panel stayed on "checking...", showed no collision
policy, left Sources empty, and its on/off switch had no handler, so
flipping it never asked for UAC and changed nothing.

Add the United Arab Emirates (same Dubai coordinates main.js already uses
for the region) and fall back to Germany for any region without a
country, so a stale value can never stop the page again.
2026-10-03 23:28:58 +02:00
Silent Mode
5f02973597 Theseus: Settings language dropdown — supported first, in their own optgroup
The dropdown mixed supported and unsupported languages in one list ranked
by global speakers, so the six languages the translator actually handles
today (en, es, fr, de, el, ru) were scattered between greyed rows. The
user had to scan every entry to find the ones they could pick. The
dropdown is now partitioned into two <optgroup>s — "Supported today"
first with the enabled languages, "Translator coming later (Accept-
Language only)" below with the greyed rows — so the top of the list
is actionable and the roadmap is still visible underneath.
2026-10-03 23:27:05 +02:00
Local Dev
71f0c96e93 Theseus: reopen the previous tabs even when history is cleared on quit
"Open previous windows and tabs" and "Clear history on quit" both default
to on, and the quit clear deleted session.json along with the history, so
every launch started from the start page and the restore setting did
nothing. The open tabs are what the user asked to reopen, not history:
while restore is on, the quit clear keeps them and still drops back/forward
and address-bar history. With restore off, the tab list is deleted as
before.
2026-10-03 23:01:56 +02:00
Local Dev
4f745406d8 Settings > Ariadne's Thread: Refresh/Install/Update live in the Status row
They had a row of their own with nothing else in it, which read as an
empty card with a stray button. Same layout as the Plug-ins list row now:
buttons beside the on/off switch.
2026-10-03 22:43:30 +02:00
Local Dev
278da658ce Merge extensions-on-first-use: add-ons start when first used, not at launch
The left-edge panels landed meanwhile, so a panel record now carries both
its side and replace-by-id; a manifest-declared panel may say side:left too,
or a dormant add-on would show its panel on the wrong edge until it starts.
2026-10-03 21:07:44 +02:00
Local Dev
de7735feb3 Theseus: quick-unlock PIN for the vault, shared with extensions
The vault re-locks on every restart and only the master password opened
it, so every extension that needs it (Aegis, now Pithos) either asked for
the master password itself or grew its own PIN. Theseus now owns one:

- Settings > Passwords sets, changes or removes a 6-digit PIN. The PIN
  wraps the master password (PBKDF2-SHA256, 600k iterations, AES-256-GCM)
  and the result is sealed with the OS keystore (safeStorage: DPAPI /
  Keychain / libsecret), so a copied vault-pin.json cannot be brute-forced
  elsewhere. Every unlock still ends at the master password.
- Three wrong PINs in a row require the master password. The strike count
  lives in the same file, so a restart does not reset it; a successful
  master-password unlock does. A PIN whose password no longer opens the
  vault (password changed) is dropped.
- unlock.html is Theseus's own prompt, over the whole window: PIN pad, or
  the master password. Extensions call api.vault.requestUnlock({ reason })
  (vault-derive capability) and get { ok } back; what the user typed never
  reaches them. Settings' locked screen offers "Unlock with PIN" through
  the same prompt.
2026-10-03 20:33:26 +02:00
Silent Mode
3536cd89bd Theseus: one language chip, auto-translate, picker owns up to what works
Two chips carried the same word in two shapes — a globe (Accept-Language)
and a translate chip (chip lights when page lang differs) — both labelled
"RU" at the same time for a Russian user. The chip for translation is
gone. The globe menu now covers both: a "Translate this page from X to Y"
item appears at the top when the loaded page is in another supported
language, flipping to "Show original" while a translation is on screen.
The chip's own code still shows the user's language (EN, RU, …); its
tooltip switches to "Translated to <X>. Menu: Show original." when a
translation is up, so the one chip reads the whole state.

With "Translate automatically" on, Theseus translates in place on
did-finish-load the first time it sees a supported source + target
mismatch for the active tab — no chip-click needed. A `_tr.autoTried`
latch keeps it to one attempt per document (a failing backend doesn't
retry on every reflow), and the latch resets on did-start-navigation so
the next page gets a fresh shot. The setting copy in Settings › Language
now says "Translate automatically" instead of "Offer to translate", so
the switch's label matches the behaviour.

The picker (both in Settings and in the globe menu) still lists every
language in WEBSITE_LANGUAGE_QUICK, but entries whose base code isn't
on the translator backend (en, es, fr, de, el, ru today) are shown
greyed out with "— translator coming later", and "Other… (Accept-Language
only, no translation)" is explicit about what free-form tags buy you.
The menu is a roadmap, not a lie: a user picking one of the greyed
entries sets Accept-Language and nothing else surprises them.
2026-10-03 19:37:04 +02:00
Silent Mode
c61fef08dc Theseus: Updates description — honest about the startup retry 2026-10-03 19:09:47 +02:00
Silent Mode
88044952e3 Theseus: Settings grows its own Language page
Website language, offer-to-translate and the translator peers list used
to sit as three sub-sections inside General, and Privacy › Anti-
fingerprinting duplicated the language picker on top of them — three
places to edit the one languageMode/languageValue setting. Settings
grows its own Language entry in the sidebar now; everything about
language — the preferred-language picker, the auto-offer toggle, the
peer list, the API key — lives there, and the Privacy duplicate is
gone. The chip in the URL bar still edits the same setting, so the
toolbar surface is unchanged.

The copy for the picker ("Your language") now says what the setting
actually drives: it's sent as Accept-Language and it's the translator's
target. Legacy "hide"/"spoof" language modes migrate to Automatic on
first open of Settings (the picker only has Automatic / a tag / Other),
so a profile that still carries one of those from an older release
lands on a valid state the first time Settings opens.

Greek (el-GR) was already in the picker; this release's silentmode.st/
libre backend added `el` to --load-only so the translator now has real
en↔el support — the picker and the backend are in step.
2026-10-03 19:05:25 +02:00
Local Dev
7254ffe6f4 Theseus: read BNS names from Ariadne's indexer; its own is the standby
Migration step 3 (DESIGN-bns-indexer-service.md). Ariadne's Thread now owns
BNS indexing on the machine, so Theseus no longer runs a second electrum
indexer beside it.

bns-indexer.js keeps its process and its messages to main.js, but inside
it is now an index host on the shared source chain:
- Ariadne's indexer over its pipe, trusted only after ariadne-helper.exe
  has checked the server process on that connection (found through
  Ariadne's uninstall key), then pushes;
- the local copies: Ariadne's files for both scopes, Theseus's own raw
  copy, the bundled one. The richest wins.
- Theseus's own index copy, written from the pipe data.

The shared core runs as Theseus's own indexer only while Ariadne is
unhealthy. That means: no pipe 4 s after launch, a pipe that fails the
check, a pipe that went silent, or an index not confirmed for 10 min while
Ariadne is not paused. The own indexer warm-starts from Ariadne's
snapshot, so there is no download and no cold sync. It hands back after
90 s of health, so a flapping service does not start and stop it. Economy
is not a failure and never triggers a takeover. With no checkable Ariadne
(portable, not installed, older than the pipe) the own indexer starts at
once, as in 0.3.70. The one thing Theseus does on Ariadne's side is run
the indexer's task at launch when "Launch at start" is off.

main.js passes the shared module paths (packaged as .mjs, which is why the
shared modules no longer import each other) and keeps the host's status.
The Ariadne panel takes its state from the indexer task when one exists,
and the sub-page says where Theseus's names come from.
2026-10-03 17:06:24 +02:00
Local Dev
4cc2d53582 Theseus: start extensions on first use, Startup switches in Settings
Every enabled add-on used to be activated synchronously in initAddons(),
during app.whenReady and before the window exists. That is the largest
launch cost left (boot tracer, 2026-10-03). An add-on can now say
"activation": "on-demand" in addon.json. It is then listed at launch but
not started. Its declared surfaces stay live: "panels" (new: sidebar
panels declared up front), toolbar-menu, context-menu-items and the
page-inject bridge, whose source is read on the first matching page.
activate() runs on first real use: a panel opened, a menu or context
item picked, a message from its panel, tab or page bridge, a Settings
addon-invoke, a wiz:// link (for Aegis). All of those go through
AddonHost.dispatch(), which starts the add-on and waits for it, so no
call is dropped. Concurrent callers share one activation, and
activations run one at a time.

Startup stays the default: the host cannot tell what an older add-on
does in activate(). request-filter add-ons and add-ons that declare no
surface are forced to startup. If activate() returns a promise, calls
wait for it (at most 5 s). api.startAtLaunch(bool) lets an on-demand
add-on ask to be started at launch again (for live relay sessions).

Converted: notepad, screenshot, translate, docx-editor, pdf-editor, vpn
(none has launch-time work: no file association, no auto-connect, and
add-on file tabs are not part of the saved session). Shield and Cookie
Pop-ups stay startup: Shield owns the request filter and must see the
first request; Cookie Pop-ups costs ~6 ms and acts unasked on every
page. Aegis stays startup and untouched: another session owns it. See
NOTE-aegis-on-demand.md (next commit).

Settings › Performance › Startup:
- "Start extensions when first used" (default on). Off = all at launch;
  switching it off starts the waiting add-ons immediately.
- "Start the wallet at launch". Shown disabled with a hint until the
  installed Aegis manifest allows on-demand. It applies with no Settings
  change once Aegis opts in.
- "Preload common menus" gates prewarmOverlays().
- "Use lightest" preset.
New keys are plain SETTINGS_DEFAULTS through the existing settings-set.
No new IPC channels.

Measured: boot-trace, fresh profile, --seconds 20 so the 30 s add-on OTA
poll can't swap Aegis mid-series; warm runs 2-3 of two paired series.
- Add-on activation at launch: 121-154 ms -> 104-174 ms. The six
  converted add-ons went from 16-20 ms to 0. The rest is Shield (83-148
  ms, noisy) and Aegis (15 ms in this tree's 0.9.0).
- Toolbar painted: 1278-1584 ms -> 1282-1481 ms (within noise).
- With "Preload common menus" off: 0 overlays prewarmed, 8 processes
  instead of 11, about 50-70 MB less at 15 s.

The bundled add-on versions are not bumped. Existing profiles keep their
old addon.json, and so stay on startup activation, until those add-ons
ship with a higher version (seedBundledAddons only reseeds a strictly
newer bundle).
2026-10-03 16:05:32 +02:00
Silent Mode
0b36532922 Theseus: translator peers list — fall back when a mirror is down
The chip ran against a single endpoint, which is the fastest way to go
dark: libretranslate.com's public tier moved behind an API key in late
2026, and most of the historical public mirrors (libretranslate.de,
argosopentech, lt.vern.cc, translate.terraprint.co) either 502 at any
given time, serve a parked page, or started requiring a key of their
own. One URL in settings meant one of those going down meant the chip
stopped working.

Settings.translateEndpoints is now an ordered list. The translator tries
each peer in order and returns the first non-error answer; a dead peer
is logged and skipped. Order is preserved — the first entry is the
primary. Shipped defaults put Silent Mode's own instances
(translate.silentmode.st, the BNS name translate.x) at the top and keep
libretranslate.com as the last-resort entry; neither Silent Mode URL
answers today, but a user's chip starts working as soon as either goes
live without a browser release.

Settings › General › Translate pages grew a list editor (same shape as
the quick-links one): PRIMARY tag on row 0, add a peer, remove any row;
bns:// URLs are accepted so a BNS translator doesn't have to be fronted
by an https host. The single-URL `translateEndpoint` setting carried
over from the earlier draft is migrated on load — a custom URL goes to
the front of the list, the historical default is dropped.
2026-10-03 15:19:09 +02:00
Silent Mode
f3c8998ecc Theseus: in-page translator (LibreTranslate client)
The Website-language setting only tells servers what the user prefers via
Accept-Language — many static sites (including names on BCDN) serve one
language and ignore it, so e.g. hello.bch loads in English for every user,
in every language. This adds a translator that converts the page's visible
text in place, so a Lithuanian user reads hello.bch in Lithuanian without
asking the server for anything.

The URL-bar grows a translate chip next to the website-language globe. The
chip lights up when the page's declared `<html lang>` differs from the
user's preferred language. Click it once to translate in place; click again
to revert — originals are kept in a renderer-local state slot and swapped
back without a reload. Right-click opens the chip menu (change target /
translator settings).

The engine lives behind a swappable adapter in main — this ships with the
LibreTranslate backend (POST /translate with {q, source, target, format}).
The endpoint defaults to the LibreTranslate public tier but is settable in
Settings › General › Translate pages, so a user with a self-hosted
LibreTranslate (or Silent Mode's own translate.silentmode.st once it is
up) swaps it there without a code change. On-device Bergamot (the WASM
engine Firefox Translations uses) will plug into the same adapter in a
later release — same contract (array of texts in, array of translations
out), the chip and revert path are already engine-agnostic.

The fetch goes through session.defaultSession.fetch so Tor and add-on
proxy rules apply uniformly, chunks the batch at ~3.8 KB per POST so a
large page spreads across several requests, times each one out at 45 s,
and reports a failure to the chip's tooltip so a dead endpoint reads as
such and not as a silent no-op. The injected walker skips SCRIPT / STYLE
/ CODE / PRE / NOSCRIPT / TEXTAREA and contentEditable subtrees, keeps a
reference to each text node and the original text, and reverts by
restoring from that pair.
2026-10-03 15:01:40 +02:00
Local Dev
adcea19f8a Theseus: background tabs stop unless kept running; popups close on click-away
A tab you switch away from is frozen (page lifecycle "frozen": no JS,
timers, network callbacks or media) one second later and thawed the moment
it is shown again. Right-click a tab → "Keep running in background" exempts
it (music, calls, dashboards); the strip marks it ▶. Dormant restored tabs
and tabs waiting on a page dialog are never frozen. Settings › Performance ›
"Stop tabs in the background" (on by default) turns it off. Freezing uses
the per-tab debugger applyFingerprint already keeps attached; Chromium only
freezes hidden pages.

The downloads, site-info and engine-picker popups close when focus moves
elsewhere in the window or to another app; the toolbar click that caused
that does not reopen them. Focus only moves into a popup while the window
is active — focusing it from the background blurs the window and closed the
popup it had just opened.

Also fixes a bug in the lazy overlays: isLoading() is still true while
did-finish-load is delivered, so a first show waited out the 4 s timeout
before appearing. Finished loads are now recorded explicitly.
2026-10-03 14:14:47 +02:00
Local Dev
08beadcf8f Theseus: close the Settings-tab vault leak and the add-on update signer bypass
A preload belongs to the WebContents, not the page: a website loaded into
the Settings tab kept window.cfg and could read every vault password, flip
settings and install extensions without consent. Settings and add-on tabs
now never load web content, and the channels behind settings-preload check
their sender. `navigate` no longer accepts calls from web pages.

Add-on updates trusted any publisherSig, whatever name it carried, even for
bundled add-ons. The trust root is now the installed addon.json (publisher,
or the operator key when there is none); versions must be plain dotted
numbers; a community install can't take over a bundled or foreign id.

Also:
- autofill matches and fills against the live URL, not a stale prov.host
- bns:// forwards the raw request path (..%2F escaped the name's bucket)
- clipboard-read denied, openExternal asks; forged collision choices ignored
- web pages can't window.open file:/chrome:/theseus:; data:/blob: no longer
  go to the search engine; the quick-links panel loses home-preload
- clear-history-on-quit is awaited and removes history.json too
- update helper takes its paths from the environment (non-ASCII profiles)
- electrum poll has a deadline; misses wait at most 2.5 s
- p records go through Tor; add-on proxy credentials are actually used
- whole-folder require-cache bust on add-on version change; failed
  activate() no longer leaks its request filter
- approvals released when the window closes; web-app ids stay on-origin
2026-10-03 09:50:10 +02:00
Silent Mode
08ecd093d2 Theseus 0.3.67: Opera-style quick-links strip on the left edge
New thin vertical column (44 px) on the left side of every page, Opera-style.
Click an icon to open its web app in a new tab; if a tab is already open on
that host, we focus it instead of stacking another one. Hidden in HTML
fullscreen so a video still fills the window; toggle via Settings › General ›
Quick links › Show the strip.

Defaults ship X, Telegram and WhatsApp. The Settings › General › Quick links
section lists the current entries with a Remove button each and a Title + URL
+ Add row that auto-prefixes https:// and auto-fills the title from the
hostname when empty. Edits write the whole settings.quickLinks array; the
strip view and the window layout react through settings-set, so no restart is
needed.

Settings › General › Updates panel also now runs standalone (no longer gated
by anything in the shared cfg.get().then() init), so a thrown exception in an
unrelated feature can't leave it stuck on "Loading…" any more — the version
line reads immediately and Check for Updates stays functional.
2026-10-02 23:49:39 +02:00
Silent Mode
cb4d900c1a Theseus 0.3.66: Updates panel is now truly independent of the shared Settings init
0.3.65 wrapped the Updates panel code in try/catch but still left it inside the
big C.get().then((s)=>…) block. If anything earlier in that block throws on a
specific profile — a feature's addEventListener on a missing element, a settings
read that rejects, anything — the panel's code never runs and the user sees
"Loading…" forever regardless of the try. Users reported this still happening on
0.3.65.

Updates panel now runs standalone right after the shared constants, as its own
immediately-invoked function, with no dependency on cfg.get() or any other
Settings init. Everything it needs (appVersion/recheckUpdate IPCs and two DOM
elements) is already available at script time. It will show "You're on vX.Y.Z"
or a specific error, never a stuck placeholder.
2026-10-02 23:04:59 +02:00
Silent Mode
a02deffe61 Theseus 0.3.65: Updates panel can't silently fatal into "Loading…" any more
Settings › General › Updates used to stay on "Loading…" forever if anything
earlier in the shared C.get().then((s)=>…) init threw, or if the version IPC
rejected — the .catch(()=>{}) on the version fetch swallowed it. Users then
had no in-app way to run "Check for updates", because the button sits in the
same panel.

Panel init now wraps in its own try, surfaces the actual error on the status
line (missing IPC / fetch rejection / init failure), and keeps the Check for
Updates button functional even when the earlier version read fails. Toolbar
chip is unaffected either way — the auto-updater runs independently.
2026-10-02 22:45:36 +02:00