System overview
Trust boundaries
- Flutter and browser-extension processes hold the master password and unlocked vault key only while the vault is unlocked.
crypto_coreowns versioned envelope formats and uses portable RustCrypto implementations of Argon2id, HKDF-SHA-256, AES-256-GCM and Ed25519. Native C ABI and browser WASM bindings share the same core implementations.- The API uses OPAQUE registration/login to issue short-lived bearer JWTs and stores opaque ciphertext revisions plus a client-encrypted, account-bound vault-key recovery envelope. Login states persisted across requests are encrypted with a separate server key. The API does not decrypt vault entries or recovery envelopes.
- PostgreSQL and cloud backups are treated as untrusted with respect to vault confidentiality.
- Native Messaging is an optional desktop integration path. The browser extension uses its own local cache and the HTTPS API when no desktop is available.
Data paths
Flutter / Browser Extension
│ plaintext stays in unlocked client memory
▼
crypto_core (key derivation + AES-GCM)
│ envelope + opaque revision metadata
▼
ASP.NET Core API ───── PostgreSQL
│
└── PostgreSQL transactions retain concurrent headsEach edit creates a new revision. The API's PostgreSQL store calls the same Rust sync_core transition validator used by the in-memory graph through a bounded, panic-safe C ABI. Rust and .NET execute shared contract vectors. PostgreSQL remains responsible for transaction locking, persistence and account cursors. A user-approved resolution revision references all heads it closes; prior ciphertext revisions remain in history. HTTP tests use both a deterministic test store and PostgreSQL integration, including concurrent merge requests.
Visible metadata
The sync service necessarily sees account identifiers, opaque record and revision IDs, parent relationships, operation times, ciphertext lengths, request timing and network metadata. Entry titles, usernames, passwords, notes and URIs are encrypted in the client payload. The product must not describe this as metadata-free storage.
Deployment boundary
The API uses PostgreSQL for encrypted revisions, OPAQUE verifier files, encrypted short-lived login states and hashed rotating refresh tokens. The production Compose stack terminates public TLS at Caddy, keeps PostgreSQL on an internal network and mounts API secrets from host-protected files. The API uses a restricted runtime database role; a separate role applies migrations. PostgreSQL backups are age-encrypted before they leave the database container, and a restore check loads each archive into a temporary database. Production startup requires the original JWT signing key, OPAQUE server setup, OPAQUE session-encryption key, database role credentials and TLS configuration. Each device has an independently revocable refresh-token family with a 90-day maximum lifetime.