theseus/ROADMAP-identity-wallet.md
Local Dev 2be859267e Theseus UX batch: tabs, address history, password autofill MVP, home cards
Six user-visible improvements + supporting infra, all uncommitted from
the earlier session-in-progress state. Ships together in one release.

Chrome / tabs
- Same-size tabs: flex 1 1 0 with max 200px, min 60px. Container gets
  overflow: hidden so many tabs shrink evenly instead of scrolling out.
- Drag-and-drop tab reordering. HTML5 drag events on each .tab; drop
  side chosen by pointer x within target (Chrome UX). New move-tab IPC
  splices the tabs array + re-emits.

Address bar
- Persistent history at userData/history.json capped at 500 LRU. Ranked
  by host-prefix > url-prefix > contains > title-contains > recency.
- Floating suggestions dropdown (addressPicker WebContentsView) anchored
  under the URL bar. Debounced 80ms input; ArrowUp/Down forward to the
  picker via address-cursor IPC; Enter fires goURL; blur closes after
  160ms so click-through registers. New files address-picker.html +
  address-picker-preload.js. Cleared by existing clearHistoryOnQuit.

Password autofill (A.2 MVP)
- Green key chip in the address bar appears when the vault is UNLOCKED
  and the active tab's host has matching credentials (exact hostname
  match for phase 1; eTLD+1 upgrade queued as A.2.5).
- Click chip → floating picker of usernames. Click a match → main.js
  runs a small script in the active tab: finds first visible
  input[type=password]:not([disabled]), walks the same form for a
  visible text/email/tel/url/search input whose name/id/autocomplete
  matches /username|user|email|login|account|id/, fills both via the
  native value setter + dispatches input/change so React/Vue-controlled
  inputs update. New files pw-fill.html + pw-fill-preload.js.
- emitPwAvailability fires from pushNav + vault setup/unlock/lock so
  the chip's visibility + count stays accurate.

Bookmarks bar
- Right-click context menu on the favorites bar. On empty area:
  "Add current page" (or "Remove current page" if already saved). On a
  specific bookmark: "Open", "Edit title…" (prompt), "Remove", plus
  the add/remove-current entry. Uses a shared .ctxmenu style mirroring
  the settings ctxmenu (dark/light aware).
- Empty-state text updated to mention right-click.

Home page
- Larger responsive card grid: auto-fill minmax(260-280px, 1fr) with
  breakpoints at 600/900/1200. Cards have a subtitle line, a colored
  badge (on-chain / Sia / server / custom), and edit affordances that
  reveal only in Edit mode.
- User-editable set: Edit toggle reveals per-card ✎/✕ + a dashed "+ Add
  card" tile. Modal for add/edit with title / URL / subtitle / badge.
  Reset-to-defaults button.
- Persisted at userData/home-cards.json. New home-preload.js exposes
  window.home = { getCards, setCards, resetCards, navigate }. IPC
  handlers in main.js validate sender.getURL() matches our own
  home.html — third-party pages see the API shape via the preload but
  can't act on the user's local cards.
- Fallback set of 2 cards renders when window.home is unavailable
  (e.g. opening home.html directly outside Electron for preview) so
  the grid is never blank.

Docs
- TheseusNavigator/ROADMAP-identity-wallet.md — the phased plan for
  the two independent strands (password manager A.2/3, browser wallet
  B.1-6). Committed earlier this session; re-listed here for context.
- TheseusNavigator/SESSION-PROMPT-identity-wallet.md — pastable
  kickoff for the next session picking up either strand.

Files added to build.files: address-picker.html,
address-picker-preload.js, pw-fill.html, pw-fill-preload.js,
home-preload.js.
2026-08-17 02:17:12 +02:00

9.2 KiB

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 for the discipline.

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.