ADR 0002: Local key storage
Keep unlocked root keys in engine-owned memory, persist only password-encrypted key material in IndexedDB, and define lock behavior.
Status: accepted for v0.1.
Keep root keys in opaque engine-owned handles while unlocked. Persist only a password-encrypted root-key envelope in IndexedDB. Locking destroys the handle and best-effort zeroes owned buffers. Never persist plaintext keys or recovery secrets in localStorage, session storage, cookies, or application servers.
ADR 0005 extends this decision with non-extractable X25519 device keys. Device private keys are
stored as structured-clone CryptoKey values in IndexedDB; they are never serialized to JSON or
exported as raw bytes. Recovery Kit passwords remain required by the default unlock flow.
Interrupted root-key rotation may persist a replacement Recovery Kit plus a replacement root key wrapped to the current non-extractable device key. The pending record is strictly parsed and must match the vault and next key version. It never stores the Recovery Kit secret, either root key in raw form, or record plaintext.
Every IndexedDB operation settles as a typed result, closes its database connection on success or
failure, and relies on transaction atomicity. A failed write does not replace the previous
encrypted record. Schema upgrades blocked by another tab return INDEXEDDB_UPGRADE_BLOCKED with a
close-tabs recovery action. Quota exhaustion returns INDEXEDDB_QUOTA_EXCEEDED; generic read,
write, and clear failures use operation-specific codes without exposing browser internals.
Malformed local records return LOCAL_VAULT_STATE_CORRUPTED and remain untouched for incident
analysis or an application-controlled recovery decision. Ownfold never silently deletes damaged
key state. Recovery still requires an intact, separately stored Recovery Kit and secret.