Issue #3594111: Owner passphrase (gate vault content with a user-held secret)
Adds the owner-passphrase feature (#3594111): an owner can gate their vault content behind a passphrase only they know, so not even the operator can read it.
Design
- Per-subject protection; two-layer wrap
MasterKEK(passphrase(Subject KEK))(Argon2id inner), so enabling never lowers at-rest protection and Master KEK rotation is unaffected. - Cross-session unlock via a split key:
Kslives only client-side (cookie/relayed header),enc_Ks(Subject KEK)server-side keyed byhash(Ks), short TTL, revocable. Losing the cookie or ending the session re-locks; the operator and a DB dump cannot open it. - Cross-site: a consumer whose read hits a locked vault gets a
vault_lockedsignal, sends the owner to unlock on the vault, gets a one-time code back, exchanges it server-to-server forKs, and relaysKsas a header (never persisted).
Owner UX
Single passphrase form (enable / rotate / disable) plus unlock / lock; a locked vault seals only the content sections (Files, Structured data) while authorization sections stay visible; unlock attempts are rate-limited (Argon2id DoS + brute force).
Notes
- Passphrase fields render as disc-font-masked text inputs (not
type=password) so browser password managers do not offer to save them; the value is blanked on rebuild so it never lands in the markup. - Kernel test coverage across pdv / pdv_server_api / pdv_client / pdv_webform.
- Deferred follow-ups: invalidate sessions on rotate; recovery code.