Earlier iterations left the nested site/hermes/* on the superseded winged-
helmet design and the Theseus addon on acid-green despite the branding
tokens moving to sky-mid. This brings every surface to the same mark + colour:
- TheseusNavigator/bundled-addons/hermes/addon.json: both icon fields
(toolbar + panel declared) swap from acid-green helmet to sky-mid
(#38BDF8) tapered-trails envelope. Picks up on next addon re-copy.
- TheseusNavigator/bundled-addons/hermes/panel.html: inline SVG swapped
from helmet dome paths to the tilted-envelope + 3 tapered trails. Uses
currentColor so the panel's own colour token drives it.
- site/hermes/branding/mark.svg + mark-stroke.svg: standard (3 trails) +
simplified 16-20px (1 trail) variants match site-hermes-x/assets/.
- site/hermes/branding/index.html: six inline mark cells (hero + 5 size
cells) swapped in bulk.
- site/hermes/index.html: hero mark SVG swapped.
Grep confirms all seven surfaces now carry 'rotate(-22 32 32)' — the
tapered-trails signature. Nothing left on the old helmet.
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.
New site-hermes-x/ — standalone .x site (own nav, own assets, own OG tags),
sibling to site-theseus-x and site-sirius-x. Internal nav stays on hermes.x:
Overview, Messenger (preview + UI mockup), Branding (brand kit). The only
cross-link is 'Get Theseus' pointing at theseus.x where appropriate.
Mark redesigned: tilted winged envelope with three tapered speed-trails on
the back-left (top longest, bottom shortest). Default colour sky #38BDF8 —
swappable via --sky in site-hermes-x/assets/shared.css. Alternate orange
palette documented but not applied by default. Mark propagated to:
- site-hermes-x/assets/mark.svg + mark-stroke.svg (16-20px variant)
- site/hermes/{index.html, branding/{index.html, mark.svg, mark-stroke.svg}}
- TheseusNavigator/bundled-addons/hermes/{addon.json, panel.html}
Theseus addon moves out of the top-right extensions dock and into the
left-strip sidebar: panels[].side = 'left' + registerSidebarPanel({side:'left'}).
Appears alongside Mail/Pithos/Telegram, excluded from rightPanels() so the
extensions dock no longer lists it. Verified via sidebarState() CDP query —
hermes:main is no longer in the right-panels list.
site/hermes/ (the nested silentmode.st/hermes page) also updated to the new
mark for consistency; both the sovereign hermes.x and the silentmode-nested
page now show the same winged envelope.
The left-column icons return to a fixed 3-column grid, each icon in its own
cell. Drag reorders which cell holds which icon, with before/after indicators
matching the right-side cards. Drops out of x,y positioning: no more free-
floating icons sticking against the card grid; defaults flow left-to-right
across the three cells. The "+" add button sits in the next free cell at
the end of the slot grid.
Homepage max-width widens to 1680px (from 1400) so on wide screens the left
column doesn't crowd the cards. Legacy x,y values on stored icons are
ignored by the renderer (array order is what shows).
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.
Shortcut: Ctrl+Shift+M -> Ctrl+Shift+E. Chrome owns Ctrl+Shift+M as
'switch profile' and E matches the Greek spelling (Ermis).
Sidebar discoverability: new bundled add-on at bundled-addons/hermes/
registers a left-strip icon + launcher panel. The panel is a thin chrome
(mark + 'Open Messages' + shortcut hint) that forwards clicks through
api.onMessage -> ipcMain.emit('hermes-open-request') -> openHermesWindow.
All real messaging code stays in main.js — add-on is purely for discovery.
Address book: opt-in vCard (.vcf) import. Dialog in the Add Contact modal
lets the user pick a file; main.js parses BEGIN:VCARD blocks for X-BNS-NAME
/ X-NOSTR-NPUB / X-NOSTR-PUBKEY plus .bch names and npubs mentioned in
NOTE fields. Candidates are previewed per-contact with checkboxes; nothing
is added until the user confirms. Non-matching vCards are returned too (so
the user can still add manually) rather than silently dropped. Nothing is
uploaded; parsing is local.
site/hermes: status badge flipped from 'designed - not yet built' to
'MVP - desktop'. New 'What ships today' grid covers threaded chat, send by
name, address book, local history (all live) and mobile + Tor (next /
planned). Hero carries the new Ctrl+Shift+E shortcut and a link to
/hermes/branding.
site/hermes/branding: new page. Mark SVG (winged envelope, two variants
for 16-20px and 24-160px display sizes), palette (reuses Silent Mode
tokens + an amber accent that's distinctly warm-yellow, not Hermes-orange
— so it never collides with Hermès the fashion house), name explanation
(English Hermes / Greek Ermis), voice notes, do/don't table, MPL-2.0
attribution.
Hermes/MOBILE-RECOMMENDATION.md: analysis of three mobile paths (inside
Ariadne / standalone / hybrid). Recommendation is Option 1: ship as a
screen inside Ariadne. Reuses the wallet seed, aligns with the design
doc's 'one seed many purposes' architecture, lowest total cost.
The left column goes back to free positioning: each icon stores its own
x,y and snaps to a 20px grid; a faint grid shows underneath while dragging
so placement feels OS-desktop. Live-move via pointer events (clicks still
navigate thanks to a 4px move threshold). Dropping a left icon over the
right card grid re-parents it as a card; dropping a card onto the left
area turns it into a free-positioned icon.
Silent Mode cards get their one-line description back under the title.
Add/edit modal gains a Description field. Defaults seed sub-text on every
Silent Mode card and x,y on every Your-links icon.
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
The left column grows from a single narrow stack to a 3-wide grid of slots
so added icons flow sideways instead of pushing the page tall. The "+" add
button sits in the next free cell of that same grid. Drag-reorder on the
left column switches from above/below to before/after indicators to match
the right-side cards; both sections are 2D grids now.
Pulls the Theseus Navigator hero (compass + title + search + .bch tip) back
to the top so identity + search stay the first thing on the page. Underneath,
the two-column desktop keeps the shape from 0.3.91 but drops the second
search bar: a narrow left column of stackable slot tiles (hello.bch,
sia.coin, Pithos.sia, Bergamot.X, + add) and a 3-column grid of nine Silent
Mode cards on the right. Right-click opens context menus — background edits
(change bg, icon size, add link/card, reset) on empty space, and copy image
/ copy link / open / edit / remove on any icon or card. Copy image renders
the brand mark to a 256px PNG and writes it with navigator.clipboard.write.
Slot order is HTML5 drag-and-drop between either column; schema is v5 and
passes through migrateCards unchanged.
Three changes to the desktop home that landed in 0.3.90:
- Hero header is back. The compass mark, Theseus Navigator title, tagline
and search sit centered at the top of the page, same shape as the home
before the desktop redesign. The Edit button moves to the top-right
corner so the hero reads as identity, not chrome.
- Silent Mode on the right is CARDS again, not just tiles. Each card
carries the brand icon on the left, title + one-line description
stacked, and a muted acid-colored badge on the right (Navigator /
Registrar / Code host / ...). Cards sit in a center-right column that
stops before the right edge, giving the left column breathing room.
- Your-links on the left is free-drag, OS-desktop style. Each icon
stores x,y and snaps to a 20px grid; dragging an icon moves it live
while the grid faintly shows underneath. The schema gains x,y on
Your-links items — unchanged for Silent Mode cards. Legacy icons
without x,y auto-layout in a tidy 2-column grid near the top.
Compass mark replaces the chain emoji in the hero. The on-disk schema
nudges to v4; v3 and v1 reads still migrate (missing x,y get an
auto-layout fallback at render time).
The new-tab page is now a two-column desktop. The right column holds the
curated Silent Mode X-family (Theseus.X, SilentMode.X, Sirius.X,
Hephaestus.X, Prometheus.X, Hermes.X, Helios.X, Ariadne.X, Aegis.X) with
real brand marks pulled from each product's own favicon or add-on manifest.
The left column is yours — hello.bch, sia.coin, Pithos.sia, Bergamot.X by
default, plus whatever you add. Icons pick up and move within and across
columns in edit mode.
Edit mode also carries per-column default-size pickers (S / M / L / XL),
six background presets + a custom colour + an image upload, and + slots
in each column. The Ariadne.X mark uses the real face/thread illustration
in ancient-Greek-pottery colours (terracotta rings on cream). The Aegis.X
tile matches the one the add-on itself ships.
The on-disk format moves from a bare array of cards (v1) to a v3 object:
{ version:3, iconSize, quickSize, background, sections:{ sm, q } }
On first read, a v1 array lands as sections.q and the Silent Mode column
stays at the code default. The remote home-cards.json URL is still
accepted as either shape; migrateCards() normalises before use.
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.
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.
The strip always drew extension panels first and web apps after them,
in a fixed order. Web apps and panels now share one order (stripOrder)
that the icons can be dragged into; reordering web apps in Settings
moves them on the strip too, and a new app or extension goes to the end.
Until the strip is rearranged, Proton Mail leads, above the panels.
A link added by hand gets an id like "youtube-k3f9", and the strip only
looked icons up by id, so YouTube or Discord added in Settings showed a
letter. The bundled marks are now also found by the site's address, and
any other site shows its own favicon (the letter when it has none).
Proton Mail leads the web apps, for new strips and where it is added to
an existing one.
An add-on's "Send on Telegram" used to open web.telegram.org in a new
tab. Theseus already has Telegram and WhatsApp in the sidebar, signed
in, so a link to the site of an app on the strip now opens in its panel
at that address (a share screen, a chat). Everything else still opens
in a tab.
Proton Mail joins the default web apps on the left strip, with its own
icon. A saved strip never picked up new defaults, so a web app added to
the defaults later is put on existing strips once; one the user removes
stays removed (quickLinksOffered).
A tab moved to its own window had no way back. Dragging a tab onto
another window's tab bar now moves it there, page and all, and the
right-click menu lists the other windows under Move to window. A
window whose last tab leaves closes.
"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.
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.
Right-click a tab and pick Move to new window, or drag it out of the tab
bar and let go off the window or over the page. The tab's own page moves
over, so sign-ins, scroll position and typed text stay as they were.
Each window has its own toolbar and tabs. The quick links, side panels
and sidebar stay in the first window and pass to another one if it
closes; the shared pop-ups (site info, downloads, password fill, page
dialogs, approvals) open in whichever window asks. Messages from a
window and events from its tabs run in that window's state, so the
existing single-window code keeps working. Closing a window's last tab
closes that window. The session file keeps every window with its place
on screen, and still opens in older builds as the first window.
Any site could call navigator.setAppBadge(n) and Electron drew n over the
app's taskbar icon. Every site wrote the same single number, so the icon
showed a count with no way to tell whose it was. A session-wide preload now
removes the Badging API in pages and service workers, so sites fall back to
their own title or favicon counters.
Logins typed in a quick link (X, Telegram, WhatsApp, …) are offered for saving, saved ones are offered and filled there, and sites that accept Theseus ID can use it from the sidebar.
Quick links in the left sidebar (X, Telegram, WhatsApp, …) open in their own
panel, which is not a tab: it had no page preload, and the password manager
and Theseus ID accepted only tabs as senders. So a login typed there was
never offered for saving, a saved one was never offered for filling, and a
site that accepts Theseus ID answered "this page cannot use Theseus ID".
The panel now gets the same preload as a web tab (iframes included), and a
stand-in tab for it lets the existing offer, save, fill and sign-in code
treat it as one more tab-like place: offers show while the panel is open,
fill goes into the panel rather than the active tab, and the prompts it
raises are queued as no tab's, so they show over whichever tab is active.
Opening Add a page… or the favorites right-click menu made the bars taller,
which pushed the page down and left an empty band under the favorites row.
The bars keep their height now; only their view grows over the page,
transparent below the bars, and the page stays where it was.
When the relays were unreachable (after a sleep, or a relay offline) the
relay client reconnected with no delay at all, re-signing the queued
wallet_ready on every lap. On Theseus's main thread that spun a core for an
hour, grew the process to 2.5 GB and froze the browser. connect() now waits
2 s, 4 s ... up to 60 s between tries, and starts over once a connection
has held for a minute.
Diagnosis: Theseus does not actually redirect to navigate.st — but for
`s3`-record names, it fetches content THROUGH /bns/<name>/ on the
gateway (Sia keys cannot ship in a public build). Since 0.3.80 that
fetch carries `x-bns-policy: client` so the gateway suppresses the
relay banner + chip; older Theseus releases got the chrome bleed-
through, which read as "redirected to navigate.st" even though the
address bar still showed https://<name>.
Two-layer defense:
Gateway:
Each injected block is now wrapped in `<!--nav-relay-X-start-->` /
`<!--nav-relay-X-end-->` sentinel comments so any downstream client
can strip it surgically without parsing the nested markup. Function
is unchanged for browsers (chrome shown by default; suppressed on
x-bns-policy: client).
Theseus main.js serveBns() s3 branch:
Already stripped the injected `<base href="/bns/...">` tag. Now also
strips any relay banner / chip / boot script found between the
sentinel comments. Catches older gateways, misconfigured deploys,
or any future edge cache that returns a chrome-bearing response.
No change to h / ip / p / u paths — those don't touch the gateway.
Other BNS-aware clients (Ariadne daemon, Ariadne mobile) that also
proxy s3 through the gateway should apply the same sentinel-based
stripping or send x-bns-policy: client. The gateway honours either.
A community extension runs under Node's permission model with read
access to its own folder, and the permission model follows symlinks
and junctions. A signed package carrying a link into the profile
(passwords.vault, the wallet store) would therefore be read access to
the profile. Windows' tar.exe fails to create symlinks only where the
privilege is withheld; with Developer Mode it creates them. Extracted
packages containing any symlink, junction, hard link or special file
are now refused, and an extension whose installed folder or scratch
folder contains one is not started (the sandbox itself cannot create
links: Node refuses symlink creation without full fs permission).
Wallet permissions were filed by the origin Theseus reports, which is
https://name for both registries. Whoever registered a colliding name in
BNS inherited the real site's connection and its silent-payment allowance,
and the other way round. A page served from BNS is now filed, and named on
every approval, as bns://name.
Grants saved before this stay under https://name: a saved connection only
discloses an address and everything else still prompts, so the BNS site of
that name simply asks again. Payment allowances are reset once, because
nobody can say which registry's site an old one was granted to.
Needs a host that reports the registry; older ones behave as before.
A page message carried only an origin, and a bns:// page is shown as
https://. The same name can be one site on BNS and another on the ordinary
web (BNS carries registrations under ICANN TLDs on purpose), so an add-on
that files permissions by origin could not tell the two apart. The call
context now carries `registry: "bns" | "web"`, also across the sandbox
boundary, and api.features.pageRegistry says the host provides it.
An extension was require()d into the browser's main process. Its declared
capabilities bound only an honest one: there it could load electron, hook
the unlock prompt for the master password, read the vault files and the
wallet's store, call the OS keystore, and reach every tab.
Extensions that do not ship with Theseus now run in a separate process, in
Node mode under Node's permission model: read access to their own folder,
write access to a scratch folder, no child processes, no workers, no native
add-ons, no Electron, and none of the browser's memory. They reach the
browser only through an allow-listed message API that calls the same
functions, with the same capability checks, as before. Built-in add-ons are
unchanged and stay in-process.
For extension authors: host calls return promises (tabs.active included);
api.require / api.import are gone, so dependencies must be bundled;
registerRequestFilter and registerSiteRoute are not offered; the store is
handed over at start and written through, so storage.get stays synchronous.
An approval raised while handling a page message is still tied to that
page's tab. If the sandbox cannot start, the extension does not run.
The channel is JSON with tagged bytes: the binary IPC format depends on the
exact V8 build on both ends.
Start of the next batch; 0.32.1 is published.
Bitcoin inputs were given the BIP125 sequence that opts in to
replace-by-fee. Aegis does not replace transactions on any chain and has
no way to bump one, so advertising a payment as replaceable only told the
recipient to distrust it until confirmed. Inputs are final again.
Since Theseus 0.3.80 the vault PIN can be 6 to 8 digits, but Aegis's
pads still submitted at six, so anyone with a 7- or 8-digit PIN got
"wrong length" from every Aegis prompt and had to use the master
password. The lock-screen, transaction and reveal pads now draw as many
dots as the host reports (pinLength) and submit at that length; a
wrong length is explained instead of shown as a wrong PIN. Aegis's own
PIN on older hosts stays at six.
- The password manager offers to save, and fills, logins in sign-in forms
embedded in iframes (payment providers, single sign-on widgets), keyed to
the frame's own site.
- PIN pads: Unlock (or Enter) beside Cancel instead of submitting on the
last digit; a new PIN is any 6 to 8 digits with Next; larger keys; calmer
hover so the pad no longer seems to flash as you type.
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.
Sign-in widgets and embedded checkouts often put the login form in an
iframe, and the password hooks only ran in a tab's top frame, so those
logins were never offered for saving or filling.
- Web tabs now run preloads in iframes (nodeIntegrationInSubFrames; pages
still get no Node). Only the password hooks act there: the home-page
bridge, window.bcnr, add-on page scripts and window.theseusId return early
outside the top frame, exactly as before.
- A login in a frame belongs to the frame's own site, taken from that
frame's committed URL. The save prompt says "login.example (in a frame on
shop.example)", the fill offer names both, and the fill goes into that
exact frame only while it is still that tab's and still on that site.
- The "did the login go through" check runs against the frame.
Verified with a shop page embedding a cross-site login frame: save offer,
fill offer and fill all target the frame; the outer page gets nothing;
top-frame logins behave as before.
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.