Files
suaveplan/MIGRATION.md
2026-05-27 23:29:53 +01:00

20 KiB

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:

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:

// @suaveplan/drive/src/adapters/storage-adapter.ts
export class StorageProviderAdapter extends StorageAdapter<StorageProviderConfig> {
  private storage: AbstractStorage;

  async connect() {
    this.storage = StorageFactory.create(this.config.provider);
    this._connected = true;
  }

  async read(key: string): Promise<Uint8Array> {
    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<Uint8Array> {
    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<void> {
    await this.storage.set(key, toUint8Array(data));
  }

  async size(key: string): Promise<number> {
    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:

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:

// @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

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

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)