# Vaulted Ecosystem — Enterprise Migration Guide > **Status: Future reference — no active OpenSpec changes.** > This document is planning for the Vaulted password-manager project, not the current genesis enterprise library migration. OpenSpec changes will be created when these packages are scheduled. > > Consolidated architecture and migration plan for building a 1Password-equivalent password manager using the Suaveplan enterprise library stack. ## 1. Executive Summary This document consolidates all architectural decisions, new package requirements, and migration steps for building **Vaulted** — a cross-platform password manager (desktop, mobile, browser extensions) on top of the Suaveplan enterprise library ecosystem. It covers: - **Technology stack** per platform (Electrobun, Capacitor+React, Bun, WebExtension MV2/MV3) - **New `@suaveplan/*` packages** required to fill critical gaps - **Storage architecture** — unifying `@suaveplan/drive` and `@suaveplan/storage-*` - **Vault orchestration layer** — the `@suaveplan/vault` package that ties everything together - **Migration sequencing** for tonight's library migration session --- ## 2. Platform Architecture Summary | Platform | Runtime | UI | Build Tool | Native Messaging | |----------|---------|-----|------------|------------------| | **Desktop** | Electrobun | React 19 + Tailwind | Bun + Turborepo | Unix socket / named pipe | | **Mobile** | Capacitor (WKWebView/WebView) | React 19 + Tailwind | Bun + Vite | In-process (Capacitor bridge) | | **Chrome** | Manifest V3 service worker | React 19 + Tailwind | Bun + Vite | `runtime.connectNative` → local socket | | **Firefox** | Manifest V2 persistent background | React 19 + Tailwind | Bun + Vite | `runtime.connectNative` → local socket | | **Safari** | Safari App Extension (embedded in macOS app) | React 19 + Tailwind | Bun + Vite + Xcode | In-process `NSExtensionContext` | **Icon generation** for all platforms is handled by `@suaveplan/iconz` (migrated from npm `iconz`), generating app/extension icons from a single source SVG. --- ## 3. Critical New Packages — Build These First These packages have no viable external alternative and unblock core authentication, 2FA, passkeys, and extension communication. ### 3.1 `@suaveplan/srp` — Secure Remote Password (SRP-6a) - **Purpose:** Zero-knowledge password authentication. Core to 1Password's auth model. - **Dependencies:** `@suaveplan/crypto-curve`, `@suaveplan/crypto-hash` - **Scope:** Client-side SRP implementation for signup, signin, session key agreement. - **Runtime:** Bun (desktop), browser (web), Capacitor (mobile) - **Estimate:** ~2-3K lines of big-integer math ### 3.2 `@suaveplan/otp` — TOTP/HOTP Generation & Verification - **Purpose:** 2FA code generation for vault items (RFC 6238/4226). - **Dependencies:** `@suaveplan/crypto-hash` (HMAC-SHA1), `@suaveplan/bytes` (base32) - **Note:** `@suaveplan/security-codes` detects OTP secrets in strings (leak detection) but cannot *generate* time-based codes. - **Estimate:** ~500-800 lines ### 3.3 `@suaveplan/cbor` — Concise Binary Object Representation - **Purpose:** CBOR encoder/decoder (RFC 8949). Required for WebAuthn/passkey support. - **Dependencies:** None - **Estimate:** ~500-800 lines, tree-shakeable ### 3.4 `@suaveplan/webauthn` — WebAuthn / Passkey Client Library - **Purpose:** CBOR attestation parsing, client data construction, COSE key parsing, assertion formatting. Monkey-patches `navigator.credentials` for browser extensions. - **Dependencies:** `@suaveplan/crypto-curve`, `@suaveplan/crypto-hash`, `@suaveplan/cbor` - **Note:** `@suaveplan/crypto-browser` provides non-extractable keys but no WebAuthn client logic. - **Estimate:** ~2-3K lines ### 3.5 `@suaveplan/native-messaging` — Encrypted Extension↔Desktop Protocol - **Purpose:** Typed, encrypted transport between browser extensions and the desktop app. - **Protocol:** X25519 handshake + ChaCha20-Poly1305 framing. Abstracts Chrome stdio, Firefox stdio, and Safari `NSExtensionContext` behind one interface. - **Dependencies:** `@suaveplan/crypto-curve`, `@suaveplan/crypto-cipher` - **Estimate:** ~1K lines --- ## 4. High-Priority New Packages ### 4.1 `@suaveplan/password-strength` — Entropy & Crack-Time Estimation - **Purpose:** zxcvbn-style password strength scoring. - **Architecture:** Two-tier — lightweight heuristic core (~5KB) + optional lazy-loaded dictionary (~200KB). - **Note:** `@suaveplan/security-codes` has Shannon entropy detection for leaked secrets, not crack-time estimation. ### 4.2 `@suaveplan/form-detection` — DOM Form Heuristics for Autofill - **Purpose:** Detect login, registration, and password-change forms with confidence scoring. - **Runtime:** Browser extension content scripts (Chrome, Firefox, Safari). - **Note:** No good external standalone library exists. Bitwarden and 1Password keep this proprietary. - **Estimate:** ~10KB tree-shakeable engine ### 4.3 `@suaveplan/site-icons` — Favicon Fetching & Caching - **Purpose:** Fetch and cache website icons (favicon.ico, Apple touch icons, manifest icons, Open Graph images) for display next to vault items. - **Dependencies:** `@suaveplan/domain-normalizer`, `@suaveplan/cache-browser`, `@suaveplan/mime` - **Note:** `iconz` (npm) is for **build-time app icon generation**, not runtime site icon fetching. ### 4.4 `@suaveplan/hibp` — Have I Been Pwned Client - **Purpose:** Watchtower-style breach monitoring via HIBP k-anonymity API. - **Dependencies:** `@suaveplan/crypto-hash` (SHA-1 prefix), `@suaveplan/retry` ### 4.5 `@suaveplan/qrcode-scan` — QR Code Decoding / Scanning - **Purpose:** Import TOTP secrets by scanning QR codes (camera or image upload). - **Note:** `@suaveplan/qrcode` is **generation-only**. This adds the decode/scan subpath. - **Decision:** Either extend `@suaveplan/qrcode` with `/scan` subpath or create standalone package. ### 4.6 `@suaveplan/biometrics` — Universal Biometric Auth Abstraction - **Purpose:** Abstract Touch ID / Face ID / Windows Hello / Android BiometricPrompt. - **Delegates:** Capacitor plugins (mobile), Electrobun native bridge (desktop), `@suaveplan/macos-keychain` (macOS) - **Estimate:** Thin facade (~300 lines) --- ## 5. Medium-Priority New Packages ### 5.1 `@suaveplan/password-generator` - Pronounceable passwords (diceware), memorable passphrases, PINs, charset-constrained generation with entropy estimation. - `@suaveplan/random` has `randomString` but no password-specific logic. ### 5.2 `@suaveplan/vault-sync` — Domain-Specific Sync & Conflict Resolution - Build on top of `@suaveplan/crdt` and `@suaveplan/diff` (JSON Patch). - Vault item merge strategies: last-write-wins for passwords, append-only for history, atomic for moves. ### 5.3 `@suaveplan/vault` — Vault Orchestration Layer *(see Section 7)* --- ## 6. Storage Architecture — Unifying Drive and Storage ### 6.1 Current Problem `@suaveplan/drive` (~7,300 LOC, 127 files) is a mature virtual filesystem with encryption transforms, chunking, deduplication, snapshots, COW, WAL, and journaling. **But it only ships with `MemoryStorage`.** `@suaveplan/storage` is a lightweight key-value abstraction with actual persistence backends: - `@suaveplan/storage-file` (Node.js/Bun filesystem) - `@suaveplan/storage-browser` (IndexedDB + localStorage) - `@suaveplan/storage-browser-opfs` (Origin Private File System) - `@suaveplan/storage-s3` (S3-compatible object storage) **The two ecosystems are disconnected.** Drive has its own `StorageAdapter` interface that is incompatible with `storage`'s `AbstractStorage`. ### 6.2 The Fix: Layered Architecture Do **not** make `storage` subservient to `drive`. They serve different abstraction levels: ```text Layer 3: @suaveplan/vault (orchestration) Layer 2: @suaveplan/drive (filesystem: encryption, chunking, snapshots, POSIX API) Layer 1: @suaveplan/storage-* (persistence backends: file, S3, IndexedDB, OPFS) ``` Drive sits **on top of** storage backends. Storage is the raw persistence substrate. ### 6.3 Required Enhancements to Storage Providers Drive's `StorageAdapter` needs capabilities that `AbstractStorage` doesn't currently expose: | Capability | Status | Implementation | |-----------|--------|----------------| | `get(key)` | ✅ Exists | — | | `set(key, value)` | ✅ Exists | — | | `delete(key)` | ✅ Exists | — | | `exists(key)` | ✅ Exists | — | | `list({ prefix })` | ✅ Exists | — | | `getPartial(key, offset, len)` | ❌ Missing | Add to `AbstractStorage`; emulate via `get` + slice for providers that can't do native partial reads | | `getItemSize(key)` | ❌ Missing | Add to `AbstractStorage`; store size in metadata or compute on demand | | `createReadStream(key)` | ❌ Missing | Add to `AbstractStorage`; emulate for providers without native streaming | | `createWriteStream(key)` | ❌ Missing | Add to `AbstractStorage`; emulate via buffering | **Effort:** ~20-50 lines per provider. `file` and `opfs` can implement natively. `indexeddb`, `s3`, `localstorage` can emulate. ### 6.4 The Universal Drive Adapter Create **one** adapter in `drive` that wraps any `storage` provider: ```typescript // @suaveplan/drive/src/adapters/storage-adapter.ts export class StorageProviderAdapter extends StorageAdapter { private storage: AbstractStorage; async connect() { this.storage = StorageFactory.create(this.config.provider); this._connected = true; } async read(key: string): Promise { const data = await this.storage.get(key); if (!data) throw new Error(`Key not found: ${key}`); return data; } async readPartial(key: string, offset: number, length: number): Promise { if ('getPartial' in this.storage) { return (this.storage as any).getPartial(key, offset, length); } const full = await this.read(key); return full.slice(offset, offset + length); } async write(key: string, data: BufferLike): Promise { await this.storage.set(key, toUint8Array(data)); } async size(key: string): Promise { if ('getItemSize' in this.storage) { return (this.storage as any).getItemSize(key); } const data = await this.storage.get(key); return data?.length ?? 0; } // ... exists, delete, list, streaming } ``` Then `configureDrive()` accepts storage provider configs directly: ```typescript const drive = await configureDrive({ storage: { type: 'storage-provider', provider: { type: 'file', config: { basePath: './data' } } }, encryption: { keyLength: 256 }, }); ``` ### 6.5 Factory Pattern Unification Backport `storage`'s module-augmentation factory pattern into `drive`'s adapter registry: ```typescript // @suaveplan/storage-file/index.ts import { StorageFactory } from '@suaveplan/storage'; import { DriveAdapterFactory } from '@suaveplan/drive'; StorageFactory.register('file', FileSystem); DriveAdapterFactory.register('file', (config) => new StorageProviderAdapter(config)); ``` Installing `@suaveplan/storage-file` automatically makes `type: 'file'` available to **both** `storage` and `drive`. ### 6.6 Migration Checklist for Storage | Step | Action | Effort | |------|--------|--------| | 1 | Add `getPartial()`, `getItemSize()`, `createReadStream()`, `createWriteStream()` to `AbstractStorage` | ~1h | | 2 | Implement enhanced methods in `storage-file`, `storage-browser`, `storage-opfs` | ~2h | | 3 | Create `StorageProviderAdapter` in `drive` | ~1h | | 4 | Wire `StorageFactory` auto-registration into drive's config | ~1h | | 5 | Backport module-augmentation pattern to drive adapter registry | ~1h | | 6 | Deprecate drive's standalone `MemoryStorage` in favor of adapter wrapping `storage` memory provider | ~30m | **Total: ~6-7 hours.** --- ## 7. `@suaveplan/vault` — The Orchestration Layer ### 7.1 The Problem `@suaveplan/keys` currently wears two hats: 1. **Key material management** (keypair generation, validation, keychain, chain signing) — correct scope 2. **Vault container management** (VaultFactory, VaultManager, packVault, unpackVault, password-based encryption) — scope creep ### 7.2 Proposed Boundaries | Package | Scope | |---------|-------| | `@suaveplan/keys` | Keypair generation, validation, keychain storage, chain signing. **No vault logic.** | | `@suaveplan/crypto-vault` | BIP39 mnemonics, Argon2id HD key derivation, 2SKD (password + Secret Key → AUK), SRP-6a, key hierarchy (AUK → personal keyset → vault key → item key) | | `@suaveplan/vault` *(new)* | Orchestrates `crypto-vault` + `keys` + `db-sqlite` + `drive` + `drive-sharing` into a single "vault" abstraction | ### 7.3 What Moves Where **Stays in `@suaveplan/keys`:** - `generateKeypair()`, `validateKeypair()`, `assertValidKeypair()` - `Keychain` class and adapters (`MemoryKeychainAdapter`, `SerializedKeychainAdapter`) - `ChainedKeySigner` - Key-type definitions (`MasterKey`, `EncryptKey`, `SignKey`, `KxKey`) **Moves OUT of `@suaveplan/keys` → `@suaveplan/vault`:** - `VaultManager` → `Vault` - `VaultFactory` → `VaultFactory` - `packVault()` / `unpackVault()` → `Vault.serialize()` / `Vault.deserialize()` - `createRootKeys()` / `createServerKeys()` → `@suaveplan/crypto-vault` (or `vault`) ### 7.4 Vault API ```typescript import { Vault } from '@suaveplan/vault'; const vault = await Vault.open({ path: '/Users/alice/.vaulted/vault', password: '...', secretKey: 'A3-XXXXXX-...', email: 'alice@example.com', }); await vault.unlock(); // Item CRUD (delegates to db-sqlite) const login = await vault.items.create({ category: 'login', title: 'GitHub', fields: [ { type: 'username', value: 'alice' }, { type: 'password', value: '...', concealed: true }, { type: 'url', value: 'https://github.com' }, ], }); // Search (delegates to db-sqlite FTS5) const results = await vault.items.search({ query: 'github' }); // Attachments (delegates to drive) await vault.attachments.upload(login.id, '/path/to/ssh-key.pem'); // Sharing (delegates to drive-sharing) const link = await vault.shares.createLink({ itemId: login.id, expiresIn: '7d', maxViews: 5, }); // Sync (delegates to sync engine) await vault.sync.pull(); await vault.sync.push(); // Lock (zeroizes keys, closes DB) await vault.lock(); ``` ### 7.5 Internal Structure ```typescript class Vault { private keyVault: MasterKeyVault; // from @suaveplan/crypto-vault private itemRepo: ItemRepository; // uses @suaveplan/db-sqlite private fileManager: FileManager; // uses @suaveplan/drive private shareManager: ShareManager; // uses @suaveplan/drive-sharing private syncEngine: SyncEngine; // new, uses @suaveplan/api + ws async unlock(password: string, secretKey: string) { // 1. Derive AUK via crypto-vault (2SKD) // 2. Decrypt personal keyset // 3. Decrypt vault keys // 4. Open SQLite DB with vault key // 5. Open drive with vault key } async lock() { // 1. Close SQLite // 2. Destroy drive session keys // 3. Zeroize all key material in crypto-vault } } ``` ### 7.6 `@suaveplan/drive-sharing` Complementarity `drive-sharing` is **not** replaced by `vault`. They layer: | Package | Scope | |---------|-------| | `drive-sharing` | Primitives: envelope encryption, partition stores, grant tokens, share links | | `vault` | Orchestration: "share this login item" → extracts from DB → encrypts via `drive-sharing` → uploads via `drive` | `vault` is the password-manager domain layer. `drive-sharing` is the secure-sharing primitive layer. --- ## 8. External Dependencies — Keep vs Build | Need | Verdict | External | Rationale | |------|---------|----------|-----------| | SRP-6a | **Build** | None good | Critical, well-defined protocol | | TOTP/HOTP | **Build** | `otpauth` pulls own crypto | Small spec, reuse our hash primitives | | WebAuthn client | **Build** | `@simplewebauthn` has deps | Needs our crypto + new CBOR package | | CBOR | **Build** | `cbor-x` is heavy | Small spec, unlocks WebAuthn | | Password strength | **Build** | `zxcvbn` is 800KB | Two-tier: light core + optional dictionaries | | Form detection | **Build** | None standalone | Proprietary in competing products | | Site icons | **Build** | None good | Domain-specific caching + fetch logic | | HIBP client | **Build** | None maintained | Small, privacy-critical wrapper | | QR scan | **Build/extend** | `jsQR` is decent | Extend existing `@suaveplan/qrcode` | | WebExtension polyfill | **Keep external** | `webextension-polyfill` | Mozilla-maintained, 15KB, excellent | | PDF generation | **Wrap external** | `pdf-lib` | Universal, tree-shakeable, ~100KB | | Image compression | **Keep external** | `sharp` (Node), Capacitor plugins | Native bindings are hard to beat | | SQLCipher | **Keep external** | `sqlcipher` native | C library, hard to replace in pure TS | | Time sync (NTP) | **Inline** | None needed | ~50 lines of UDP code | | YubiKey/WebHID | **Defer** | Platform-specific | Hardware integration, post-MVP | --- ## 9. Migration Sequencing for Tonight ### Phase 1: Storage Bridge (6-7 hours) 1. Enhance `AbstractStorage` with `getPartial()`, `getItemSize()`, streaming 2. Implement in `storage-file`, `storage-browser`, `storage-opfs` 3. Create `StorageProviderAdapter` in `drive` 4. Wire auto-registration so storage providers are available to drive 5. Test: `configureDrive({ storage: { type: 'file', ... }})` works end-to-end ### Phase 2: Vault Rearchitecture (4-5 hours) 1. Strip vault logic from `@suaveplan/keys` (move to `@suaveplan/vault`) 2. Ensure `@suaveplan/crypto-vault` owns key hierarchy + 2SKD 3. Scaffold `@suaveplan/vault` with `Vault.open()`, `unlock()`, `lock()` 4. Wire `vault.items` → `db-sqlite`, `vault.attachments` → `drive` ### Phase 3: Critical Crypto Packages (5-6 hours) 1. `@suaveplan/cbor` — encoder/decoder 2. `@suaveplan/srp` — SRP-6a client 3. `@suaveplan/otp` — TOTP/HOTP generation 4. `@suaveplan/webauthn` — WebAuthn client (depends on CBOR) ### Phase 4: Extension Infrastructure (3-4 hours) 1. `@suaveplan/native-messaging` — encrypted transport 2. `@suaveplan/form-detection` — DOM heuristics 3. `@suaveplan/password-strength` — heuristic + dictionary scorer ### Phase 5: Utilities (2-3 hours) 1. `@suaveplan/site-icons` — favicon fetch/cache 2. `@suaveplan/hibp` — breach monitoring client 3. `@suaveplan/qrcode-scan` — QR decode subpath 4. `@suaveplan/biometrics` — universal biometric facade 5. `@suaveplan/iconz` — migrate from npm, integrate into build pipelines **Total estimate: 20-25 hours across all phases.** Not a single night, but Phase 1+2 (storage bridge + vault rearchitecture) is the critical foundation and can be done in one focused session. --- ## 10. Security Invariants These must hold across all packages: 1. **Key material never leaves `@suaveplan/crypto-vault` / `@suaveplan/keys` memory space.** `vault`, `drive`, `db-sqlite` only receive derived encryption keys, not the AUK or mnemonic. 2. **All key material is zeroized on `vault.lock()`.** Use `@suaveplan/crypto-utils` `zeroize()` on `Uint8Array` instances. 3. **Decryption happens exclusively on the client.** The sync server only ever sees encrypted blobs. 4. **Biometric data never leaves the hardware secure enclave.** Platform APIs return boolean only. 5. **Extension crypto is delegated to the desktop app.** Browser extensions never hold vault keys. --- ## 11. References - `../vaulted/REPORT.md` — 1Password architecture analysis - `../vaulted/apps/desktop/REPORT.md` — Electrobun desktop build spec - `../vaulted/apps/mobile/REPORT.md` — Capacitor mobile build spec - `../vaulted/plugins/chrome/REPORT.md` — Chrome MV3 extension spec - `../vaulted/plugins/firefox/REPORT.md` — Firefox MV2 extension spec - `../vaulted/plugins/safari/REPORT.md` — Safari App Extension spec - `../suaveplan/README.md` — Full package inventory (356 packages) - `STATUS.md` — Dashboard table of every package's migration status - `SUMMARY.md` — Per-package summaries (completed / pending / genesis-original)