connection-vault.md 5-13 as code, with the implementation-time decisions recorded in a new section 18 and vault-ux.md holding the agreed UX walkthroughs as the authorization layer's acceptance criteria. Capsules gain paired optional fields connection_vault_root and vault_sudo_key (both or neither, validated); registration mints them, login/change-password/recovery carry them forward, and enableConnectionVault is the phrase-gated migration for existing accounts -- recovery capsule written first, fail closed, idempotent by reusing roots found in either capsule so a partial failure can never mint twice and strand records. src/vault/ holds the finalized key hierarchy (HKDF/ScalarExpand constants pinned by test vectors that must only ever be reverted, never updated), per-connection encrypted records signed by a derived vault identity with the capsule JCS+padding+AES-GCM pipeline and shared buckets so record types are indistinguishable by size, the NWC profile with lossless URI parse/export, relay sync reusing the publish-and- verify quorum bar, and per-record high-water marks so a stale replica is withheld and reported rather than silently resurrecting a revoked credential. The two-tier model makes sudo mode cryptographically real: personal- tier record keys require vault_sudo_key as HKDF input, VaultSession holds it only inside an enableSudo/endSudo window (wiping on the way in and out), and outside a window personal records are invisible -- the same trial-decrypt returns null, not an error. The app-facing API is structurally blind to the tier by construction. 118 tests passing, including four end-to-end scenarios against mock relays: clean-device restore, password-change carry, legacy-account migration (wrong phrase refused), and rollback detection. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01CMr9Yt8SYmZcfgrg3CV2G1
4.5 KiB
Connection Vault — UX walkthroughs
Status: Agreed product direction (2026-07-30)
Role: These scenarios are the acceptance criteria for the Phase D
authorization layer (connection-vault.md §12, §18). The design principle
running through all of them: friction appears exactly where danger is, and
nowhere else. Count the friction across the whole set: one phrase entry
ever, one budget-facing tap per app, one tap per new device, sudo or phone
approval only at genuinely dangerous reveals. Everything else is silence.
1. Turning the vault on — existing account
Account manager card: "Connection Vault — store wallet and storage
connections once, use them on every device." Enabling asks for the recovery
phrase with the reason stated plainly ("the vault must survive both your
password and your phrase"), rewrites both capsules with quorum readback, and
fails closed with a retry that reuses the same roots
(enableConnectionVault). New accounts skip this scenario entirely — the
roots are minted at registration.
Deliberate friction: the phrase entry, once per account ever.
2. Connecting a wallet the first time — inside an app
The app calls the request API with a reason string. The sheet walks the user through pasting/scanning an NWC URI, parses it, and shows what it learned — provider, relay, and the budget in large type. No budget draws an amber warning recommending a budgeted connection, never a hard block. Label pre-filled from the app name. The final line before reveal is honest: "[App] will receive this connection and can spend within its budget until you revoke it." Save binds the record to the requesting origin.
Deliberate friction: one explicit share tap, budget staring at the user. Removed friction: naming, binding, storage.
3. The payoff — new phone
Sign into the app's BitLogin, and at the first payment moment:
Use "Satisfied spending wallet"? You connected this wallet to Satisfied on another device. Budget: 500 sats/day.
One tap. Zero pasting. This screen is the entire reason the vault exists. A new device seeing a credential for the first time must never happen silently, even with a durable grant.
4. Every ordinary day after
Nothing. No prompt, no branding. The grant is durable per origin per device; the vault's best UX is absence.
5. Connected apps — management
Account manager page listing origin → connection → grant date → devices, with Revoke access per row and one "Revoke all app access" panic button. Each connection's detail page separates two red actions honestly:
- Remove from BitLogin — deletes the stored copy; the credential itself keeps working for anyone who already holds it.
- Revoke at wallet/provider — an instruction with a guide, not a button pretending BitLogin can reach into the wallet (§11).
6. S3 connections
Same sheet pattern; the import screen teaches while it collects: scoped IAM key vs root key question, a recommended object-prefix field, amber warning on unscoped credentials. Reveal to an app requires sudo mode — S3 secrets guard buckets, not a 500-sat budget.
7. Personal tier — passwords and secure notes
Exists ONLY in the first-party account manager. No app can request, list, or learn these records exist (the tier is invisible to the app-facing API by construction — see §18.2). Viewing requires sudo mode: re-enter the account password, ~5-minute window. Copy button, auto-clearing field. The copy is honest: a safe place for a few important secrets, not a password manager — autofill and site-matching wait for the extension generation. Seed phrases stay banned.
8. Phone approval (after §SF5–7 ships)
High-tier reveals push to the enrolled phone; the record key is 2-of-2 derived so a laptop cryptographically cannot proceed on its own. Budgeted NWC connections deliberately do NOT phone-prompt — the budget is already the lock, and prompting every payment trains reflexive approval.
9. Disaster — phrase-only recovery
Twelve words on a clean device restore identity, then the vault roots ride the recovery capsule (§13.2), so connections reappear: wallets, buckets, passwords. The phrase remains the ungated master path (§SF9). The flow nudges re-enrolling a phone and rotating anything sensitive.
Build order for Phase D
The screens, in dependency order: request/consent sheet (2, 3) → guided NWC import (2) → Connected apps + connection detail (5) → S3 import (6) → sudo mode (7). The personal tier and phone approval layer on later without reshaping anything before them.