Security

Last updated 2026-09-15.

Zero-knowledge encryption

K = Argon2id(passphrase, salt), then two independent keys via HKDF-SHA256: an authKey sent to the server as your login credential, and a KEK that never leaves your device. The KEK wraps a per-account data encryption key, which in turn encrypts every sensitive field (AES-256-GCM) before it’s sent. A compromised server exposes ciphertext and metadata - card list, due dates - never balances, merchant names, or notes.

No password reset, on purpose

The server never learns your passphrase, so it has nothing to reset. Account recovery uses a 24-word recovery phrase, generated once at signup and shown exactly once - it wraps the same data key independently of your passphrase. Losing both means the data is gone, including to whoever operates the deployment.

Passkeys and two-factor authentication

WebAuthn passkeys and TOTP two-factor are both available (Settings > Security). Both strengthen sign-in, but neither replaces your passphrase for unlocking data on a given device - only the passphrase can derive the key that does that, by design.

Transport & headers

HSTS, a nonce-based Content-Security-Policy, Referrer-Policy: no-referrer, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, a restrictive Permissions-Policy, and same-origin COOP/CORP on every response.

Rate limiting & CSRF

5 requests/minute on authentication routes, 60/minute per session and per hashed IP on the app’s API. Every mutating API request carries a double-submit CSRF token.

Audit log

Sign-ins, lockouts, passkey and two-factor changes, passphrase changes, and card create/delete are logged to an account-scoped audit trail (Settings > Audit log) - never the contents of a card, balance, or transaction.

Dependency & code scanning

Every change runs through Semgrep’s OWASP Top Ten ruleset, pnpm audit at the high-severity threshold, and an OWASP ZAP baseline scan in CI - none pass with a known High or Critical finding.

What isn’t mitigated

Said honestly rather than implied away: a malicious browser extension with page access, a global adversary, a hosting provider with hypervisor-level access, or someone with direct physical access to an unlocked device. Zero-knowledge encryption protects data at rest and in transit to a server that can’t read it - it doesn’t protect a device that’s already been compromised while you’re using it.