Scope
The zero-knowledge platform: crypto envelope, key lifecycle, tenant isolation, auth, and the AI path. Derived from the rev. 3 plan's 4-persona adversarial review (all must-fixes implemented or tracked below).
Threat model
| Threat | Control |
|---|---|
| Full server/DB compromise | Server holds ciphertext + salts + wrapped keys only; no decryption possible |
| Cross-tenant read (app bug) | Postgres FORCE RLS, non-owner role, SET LOCAL GUC per transaction; negative test |
| Multi-device nonce reuse | XChaCha20-Poly1305 192-bit random nonces (not AES-GCM) |
| Envelope downgrade/tamper | Header bound as AAD; unknown versions rejected |
| Password oracle | Verifier = AEAD-sealed constant; wrong password fails tag, uniform error |
| Account takeover via email claim | Identity keyed on immutable (iss,sub), email_verified required |
| Dedupe-key enumeration | HMAC/keyed-hash derivation — server can't confirm a record id it hasn't seen |
| Coach plaintext exposure | Client-direct to Gemini (scoped short-lived cred); server never in plaintext path |
| Malicious web bundle (server pushes JS) | Known weaker tier vs native: CSP/SRI planned; reviewed native binary is the anchor |
Encryption
| Layer | Mechanism |
|---|---|
| KDF | Argon2id, per-user 16B salt, params pinned + carried in vault (ops=3, mem=64MiB baseline) |
| Key wrap | XChaCha20-Poly1305, AAD lifebot/wrap/v1; recovery key = second high-entropy wrap (planned) |
| Records | XChaCha20-Poly1305-IETF, AAD-authenticated header, compact nonce.ct blobs |
| Transit | TLS everywhere (Cloudflare Tunnel ingress; loopback-only host binds) |
| At rest (defense-in-depth) | Postgres on the box; LUKS recommended; backups gzip + vaults-integrity check |
GDPR product features
- Consent before coach disclosure; minimised summary only.
- Export + erasure planned as
GET /v1/me/export,POST /v1/me/erase; crypto-erasure by wrapped-key destruction. - Special-category minimisation: cleartext metadata is routing-only (opaque ids, counters, timestamps).
Residual risks
Audited 2026-07-26 — see lifebot-v2/security-review. The first risk below was confirmed as the audit's single critical finding: CSP/SRI were never added, so the web tier's trust-on-every-load defeats the "operator can never decrypt" claim outright. Several controls tabulated above are also aspirational rather than implemented —
email_verifiedis dead code, the recovery key does not exist, and FORCE RLS is bypassed by the superuser credential the API container holds. Treat this page as the intended design and the review as the actual state.
- Web-tier trust-on-every-load (server-shipped JS) — mitigate CSP/SRI; document tier honestly.
- Recovery key not yet implemented — forgotten password currently = data loss (by design, but UX must say so).
- Gemini sees the consented summary — vendor trust bounded by paid-tier no-training terms + DPA (Stage 3).
- In-app spend cap is per-deployment today; per-tenant caps land with the control plane (Stage 3).
Related
wiki/projects/lifebot-v2/security-design.md · git is the source of truth