Status
Accepted
Context
The same key must be reproducible on web, iOS, and Android (owner requirement), and the server must not be able to compute it. Device hardware keys don't travel; OAuth tokens are visible to the IdP/server. Multi-stack crypto (WebCrypto + RN modules + Python) risks byte-level divergence, and AES-GCM's 96-bit nonces are unsafe for uncoordinated multi-device writers.
Decision
Master password + per-user salt → Argon2id → master key → unwraps a random data key;
payloads use XChaCha20-Poly1305 with an AAD-authenticated header. One library family —
libsodium (JS sumo/WASM, PyNaCl, react-native-libsodium later) — injected per platform into a
single shared TS implementation (setSodium). Identity (Clerk) and unlock (master password)
remain two separate secrets.
Consequences
- Proven byte-identical: TS-encrypted vectors decrypt in Python (spike, 8/8 checks).
- 192-bit random nonces make multi-device nonce reuse cryptographically negligible.
- Forgotten password = data loss unless the user keeps the recovery key; no server bypass exists.
- Pinned KDF params travel in the vault, enabling per-user upgrades without breaking old envelopes.
wiki/projects/lifebot-v2/adr/0002-one-libsodium-master-password-key.md · git is the source of truth