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
|
|
|
# Identity + wallet roadmap
|
|
|
|
|
|
|
|
|
|
Two independent strands, both rooted in the same BCH-style seed-with-hardened-
|
|
|
|
|
derivation model. Order them however makes sense to the session picking this
|
|
|
|
|
up; strands don't block each other.
|
|
|
|
|
|
|
|
|
|
**Strand A — Password manager.** Local-first vault, deterministic derivation
|
|
|
|
|
per site+username, optional Sia backup for cross-device.
|
|
|
|
|
**Strand B — Browser-integrated BCH wallet.** MetaMask-style provider so
|
|
|
|
|
Silent Mode pages (and any BCH-native dApp) can request signatures,
|
|
|
|
|
payments, and BCNR name operations from the user's wallet.
|
|
|
|
|
|
|
|
|
|
Both use the SAME seed via distinct hardened purpose subtrees so one
|
|
|
|
|
compromise can't leak the other:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
m / 44' / 145' BCH wallet (SLIP-44) ← wallet already ships
|
|
|
|
|
m / 1381' / 0' passwords ← phase 1 shipped
|
|
|
|
|
m / 1414' / 0' messenger (Nostr etc.) ← future
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Same seed, three purposes, no cross-derivation. See
|
|
|
|
|
[DESIGN-password-manager.md](DESIGN-password-manager.md) for the discipline.
|
|
|
|
|
|
2026-09-06 12:36:53 +02:00
|
|
|
**Add-on roots (decided 2026-09-06, do not change).** The vault never stores
|
|
|
|
|
the BIP-39 seed, only per-purpose roots, so add-ons cannot get a fresh
|
|
|
|
|
seed-level purpose after setup. `api.vault.derive("<addonId>/<path>")`
|
|
|
|
|
therefore returns HKDF-SHA256(passwords/0 root, info =
|
|
|
|
|
`silentmode/addons/<addonId>/<path>`). The bundled BCH wallet uses
|
|
|
|
|
`bchwallet/mainnet/0` as its BIP32 master seed and derives `m/44'/145'/0'`
|
|
|
|
|
from it. This is deterministic from the mnemonic (mnemonic → seed →
|
|
|
|
|
passwords/0 → wallet) and works for random-seed vaults too. Changing the
|
|
|
|
|
info string or the parent root would orphan every wallet's funds; recovery
|
|
|
|
|
without Theseus is via the account xprv shown in the wallet's Settings.
|
|
|
|
|
|
2026-09-06 18:49:33 +02:00
|
|
|
The bundled Siacoin wallet follows the same rule with `siawallet/mainnet/0`
|
|
|
|
|
as the 32-byte seed for walletd's `KeyFromSeed(seed, index)`; addresses are
|
|
|
|
|
standard unlock hashes. It talks to a user-configured walletd node (no
|
|
|
|
|
default endpoint ships in the add-on — hosted providers put an access key
|
|
|
|
|
in the URL path).
|
|
|
|
|
|
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
|
|
|
## Strand A — Password manager
|
|
|
|
|
|
|
|
|
|
### A.1 — SHIPPED (0.0.3, 9c09955e)
|
|
|
|
|
- Local encrypted vault at `<userData>/passwords.vault`
|
|
|
|
|
- AES-256-GCM under PBKDF2 (200k iters, SHA-256) master-password key
|
|
|
|
|
- BIP39 mnemonic → seed → HKDF purpose root
|
|
|
|
|
- Deterministic derivation per (domain, username, version, rules)
|
|
|
|
|
- Settings UI: setup / locked / unlocked panels, add/edit/reveal/copy/delete
|
|
|
|
|
- IPC surface: pwStatus/Setup/Unlock/Lock/List/Get/Add/Update/Remove/Generate
|
|
|
|
|
- 12 crypto unit tests, all passing
|
|
|
|
|
|
|
|
|
|
### A.2 — Autofill (~ 1 week)
|
|
|
|
|
The user-visible next step. Right now users must copy from Settings.
|
|
|
|
|
|
|
|
|
|
- **ContentScript** injected on every page (Electron
|
|
|
|
|
`session.defaultSession.setPreloads` OR per-webContents preload extension)
|
|
|
|
|
- Watch `input[type=password]` + adjacent username field on load AND on
|
|
|
|
|
DOM mutation (SPAs). Debounce.
|
|
|
|
|
- **Origin binding via eTLD+1** using the public suffix list. Ship a
|
|
|
|
|
bundled snapshot of https://publicsuffix.org/list/public_suffix_list.dat
|
|
|
|
|
under `Argus/data/` so `evil-google.com` can't fill `google.com`.
|
|
|
|
|
- **Toolbar affordance**: a key icon in the address bar OR the shield's
|
|
|
|
|
overlay grows a "Passwords" chip when entries exist for the current
|
|
|
|
|
origin. Click → pick which credential to fill.
|
|
|
|
|
- **Keyboard shortcut**: Ctrl+Shift+L to fill the current form.
|
|
|
|
|
- **Save prompt**: when a form submit yields a password field the vault
|
|
|
|
|
hasn't seen, prompt "Save this password? (Generate strong one instead?)"
|
|
|
|
|
- **Per-origin permission** cached: sites that were already refused don't
|
|
|
|
|
re-prompt.
|
|
|
|
|
- Tests: origin-binding table (eTLD+1 for suffix corner cases —
|
|
|
|
|
`*.co.uk`, `*.compute.amazonaws.com`, IDN), autofill DOM injection
|
|
|
|
|
vector coverage.
|
|
|
|
|
|
|
|
|
|
### A.3 — Sia backup / cross-device restore (~ 3-5 days)
|
|
|
|
|
Opt-in cloud sync of the encrypted vault blob.
|
|
|
|
|
|
|
|
|
|
- New setting: "Sync vault to Sia". Off by default.
|
|
|
|
|
- User provides their own Sia S3 endpoint + credentials (like the operator's
|
|
|
|
|
`sia-s3.json`), OR opts to use the operator's public relay at
|
|
|
|
|
`sia://silentmode.st/vault/<vaultId>` (metadata leaks: only "this
|
|
|
|
|
vaultId exists", nothing else — vault is encrypted end-to-end).
|
|
|
|
|
- On save: upload the encrypted-blob bytes to `bucket/vault-<id>.bin`.
|
|
|
|
|
Include an integrity hash in the local vault header.
|
|
|
|
|
- On new-device setup: user picks "Restore from Sia" → enters vaultId +
|
|
|
|
|
master password → download blob → decrypt.
|
|
|
|
|
- **Explicit non-goal**: no operational sync (last-write-wins across
|
|
|
|
|
devices, no merge). Phase A.3 is "clone the vault to another laptop",
|
|
|
|
|
not "keep two laptops perfectly in step". Multi-device concurrent-edit
|
|
|
|
|
is A.4 if we ever want it.
|
|
|
|
|
|
|
|
|
|
### A.4 — Nice-to-haves
|
|
|
|
|
Order these however makes sense; each is small and independent.
|
|
|
|
|
- Import from Bitwarden JSON / KeePass CSV / Firefox CSV
|
|
|
|
|
- Export to same
|
|
|
|
|
- Password strength meter on the paste-a-password entry
|
|
|
|
|
- Auto-lock after N minutes of Theseus idle
|
|
|
|
|
- Password-history per entry (previous versions kept, so a rotation
|
|
|
|
|
doesn't lose the old password until the user confirms it's no longer
|
|
|
|
|
needed anywhere)
|
|
|
|
|
- Compromised-password check via HIBP k-anonymity API (opt-in, sends
|
|
|
|
|
a 5-char SHA-1 prefix only)
|
|
|
|
|
- Password-strength / composition rules from the site's `passwordrules`
|
|
|
|
|
attribute (WICG spec) so generated passwords satisfy site policy
|
|
|
|
|
|
|
|
|
|
## Strand B — Browser-integrated BCH wallet
|
|
|
|
|
|
|
|
|
|
MetaMask-style `window.bcnr` (or `window.bch`) provider that lets a page
|
|
|
|
|
request signatures, payments, and name operations from the user's wallet.
|
|
|
|
|
The wallet already exists in `site/js/` for the registrar flow; this
|
|
|
|
|
strand generalises it to a per-origin API for any page.
|
|
|
|
|
|
|
|
|
|
### B.1 — Design (start here) (~ 2-3 days)
|
|
|
|
|
|
|
|
|
|
Ship the doc before writing code. Two hard problems to nail:
|
|
|
|
|
|
|
|
|
|
1. **Permission model**. Chrome dapp providers have taught us how easily
|
|
|
|
|
this becomes malware infrastructure. The doc must lock down:
|
|
|
|
|
- Permission granularity: per-origin, per-method, per-amount
|
|
|
|
|
- First-use always shows a modal (never auto-approves)
|
|
|
|
|
- Persistent permissions revocable in Settings > Wallet > Sites
|
|
|
|
|
- "You're on a BCNR name backed by an on-chain contract" vs "You're
|
|
|
|
|
on random-site.com" — trust badges the user can see before signing
|
|
|
|
|
- Anti-phishing: display the recipient's cash address in the
|
|
|
|
|
confirmation dialog with a big warning if the address changes
|
|
|
|
|
mid-session
|
|
|
|
|
|
|
|
|
|
2. **API surface**. Match existing convention where possible so BCH dApp
|
|
|
|
|
developers don't learn a Silent Mode-only dialect:
|
|
|
|
|
- `bcnr.getAccounts()` — returns cash addresses the user has approved
|
|
|
|
|
for this origin
|
|
|
|
|
- `bcnr.requestAccount()` — first-use modal
|
|
|
|
|
- `bcnr.signMessage({message, address})` — BIP-137 style, used for
|
|
|
|
|
BCNR name registration and auth flows
|
|
|
|
|
- `bcnr.sendPayment({to, amount, memo})` — modal-confirmed BCH send
|
|
|
|
|
- `bcnr.resolveName(name)` — no permission needed; BCNR lookup
|
|
|
|
|
- `bcnr.registerName({name, records})`, `bcnr.updateName(name, records)`
|
|
|
|
|
— modal-confirmed; ties into the site/register.html flow
|
|
|
|
|
|
|
|
|
|
Deliverable: `TheseusNavigator/DESIGN-integrated-wallet.md` in the same
|
|
|
|
|
shape as `DESIGN-password-manager.md`.
|
|
|
|
|
|
|
|
|
|
### B.2 — Injection scaffold + read-only APIs (~ 1 week)
|
|
|
|
|
|
|
|
|
|
- ContentScript that injects `window.bcnr` on every page load. Isolated
|
|
|
|
|
world (Electron's `contextIsolation: true` already enforces this) so
|
|
|
|
|
page JS can't tamper with the provider.
|
|
|
|
|
- Provider is an event-emitting object that speaks JSON-RPC over
|
|
|
|
|
postMessage to the preload, which forwards to main via IPC.
|
|
|
|
|
- Read-only methods (no permission needed): `resolveName`, `getBcnrTlds`,
|
|
|
|
|
`isRegistered`, `getRecordVersion`.
|
|
|
|
|
- Tests: page-script → provider → main round trip; provider surface
|
|
|
|
|
matches the spec.
|
|
|
|
|
|
|
|
|
|
### B.3 — Signature + payment APIs (~ 2-3 weeks)
|
|
|
|
|
|
|
|
|
|
- Permission-gated: `requestAccount`, `signMessage`, `sendPayment`.
|
|
|
|
|
- Reuse the existing wallet in `site/js/` (libauth-based) as the signing
|
|
|
|
|
core; wrap it in a main-process handler that reads the wallet's
|
|
|
|
|
`wallets.json` (already encrypted with the wallet passphrase).
|
|
|
|
|
- Modal confirmations rendered as a new WebContentsView overlay (same
|
|
|
|
|
pattern as popover/engine-picker/downloads).
|
|
|
|
|
- Site permissions persisted in `userData/wallet-permissions.json`.
|
|
|
|
|
- Settings → Wallet section: list connected sites, revoke per-site or
|
|
|
|
|
per-method.
|
|
|
|
|
|
|
|
|
|
### B.4 — BCNR name operations (~ 1 week)
|
|
|
|
|
|
|
|
|
|
- `registerName`, `updateName` — thin wrappers over the existing
|
|
|
|
|
`Argus/src/register.js` / `update.js` flows.
|
|
|
|
|
- Sanity: same modal-first pattern; show the exact record payload before
|
|
|
|
|
signing (users need to see what's going on-chain permanently).
|
|
|
|
|
|
|
|
|
|
### B.5 — Nostr messenger integration (~ 2-3 weeks)
|
|
|
|
|
|
|
|
|
|
Bring the messenger strand in as a parallel purpose. Same seed under
|
|
|
|
|
`m/1414'/0'`, so users who set up passwords in strand A already have
|
|
|
|
|
messenger keys.
|
|
|
|
|
|
|
|
|
|
- Derive Nostr keys (secp256k1, npub/nsec encoding) from the messenger
|
|
|
|
|
purpose root
|
|
|
|
|
- `window.nostr` NIP-07 provider (widely-supported protocol, dozens of
|
|
|
|
|
compatible clients)
|
|
|
|
|
- Chrome integration: address bar chip when a Nostr-native site connects
|
|
|
|
|
- Threat model: Nostr keys don't hold funds, but do bind identity —
|
|
|
|
|
compromise = impersonation. Same modal-first permission model.
|
|
|
|
|
|
|
|
|
|
### B.6 — Advanced: connect-wallet UX
|
|
|
|
|
|
|
|
|
|
- QR-pairing so a mobile Ariadne can act as a hardware-wallet-like
|
|
|
|
|
signer for a desktop Theseus
|
|
|
|
|
- Multisig confirmations (2-of-3 with a hardware wallet + Theseus +
|
|
|
|
|
Nostr-relayed remote signer)
|
|
|
|
|
|
|
|
|
|
## Cross-strand dependencies
|
|
|
|
|
|
|
|
|
|
- Both strands rely on the crypto module at
|
|
|
|
|
`Argus/src/lib/password-vault.js`. Extract `bip39ToSeed` +
|
|
|
|
|
`seedToPurposeRoot` into a `seed-derive.js` module the wallet strand
|
|
|
|
|
can also import cleanly.
|
|
|
|
|
- Both strands should share the master-password unlock. If a user has
|
|
|
|
|
the vault unlocked and the wallet locked (or vice versa), the second
|
|
|
|
|
unlock should not re-prompt.
|
|
|
|
|
|
|
|
|
|
## Non-goals
|
|
|
|
|
|
|
|
|
|
- No third-party cloud sync. Sia is the only sync target we bless.
|
|
|
|
|
- No Chromium password-manager UI reuse. Ergonomics don't fit our threat
|
|
|
|
|
model (Google Sync-shaped).
|
|
|
|
|
- No Ethereum / Solana / other-chain support in Strand B. This is BCH-
|
|
|
|
|
specific; multi-chain is a separate future decision.
|
|
|
|
|
- No hardware-wallet abstraction layer in phase 1. Ledger/Trezor
|
|
|
|
|
integration is a phase 4 conversation.
|
|
|
|
|
- No autofill for credit cards, addresses, or contact forms. Each has
|
|
|
|
|
security nuances that deserve their own design pass. Password-only.
|