Expand description
Encrypted-at-rest Sanctuary store with an independent decoy lane (master plan §6).
Unlike the projection filter in super::sanctuary (which merely hides journal rows on
read), this is a real boundary: sensitive notes live only inside AEAD-encrypted lanes,
keyed by material derived from the owner’s PIN with Argon2id (memory-hard; ADR D1) — or
PBKDF2-HMAC-SHA256 for a PBKDF2-configured vault — over a per-lane random salt. When the vault
is not unlocked there is nothing readable on disk — not a filtered view, actual ciphertext.
The on-disk format is the CBOR-native, n-layer VaultContainerV2 (vault v2, ADR §2/§9).
There is no JSON path anywhere in this module: the container and the per-lane records are
both ciborium-encoded. The container carries a constant number of layer slots (padded with
reserved layers) so the on-disk layer count reveals nothing about how many lanes are real /
decoy / empty.
Two independent lanes are in use:
- Real — the true Sanctuary, opened by the real PIN.
- Decoy — a separate encrypted lane with its own salt/key, opened by the duress PIN. It never aliases real data (different key, different ciphertext) and a duress unlock only ever touches the decoy lane.
The PIN is never stored, not even hashed. A per-lane verifier (a fixed magic string encrypted under the lane key) is used to recognise which lane a PIN belongs to. There is no destructive “nuke PIN” (plan §6).
Native-only: qualia_core_db::crypto::sanctuary_crypto is not(wasm32); the desktop is the
authoritative node that owns keys and the vault.
Structs§
- Decoy
Action View - One decrypted decoy action surfaced to the real lane.
- Decoy
Activity Report - The result of reviewing the decoy audit log from the real lane.
- Sanctuary
Vault Note - A sensitive note held only inside the encrypted vault.
Enums§
- Audit
Integrity - The integrity verdict of the decoy audit log at review time.
- Sanctuary
Lane - Which lane a PIN opened.
Constants§
- SANCTUARY_
VAULT_ FILE - On-disk vault file. CBOR, not JSON (vault v2).
Functions§
- add_
note - Append a note to the lane the PIN opens (real PIN → real lane; duress PIN → decoy only).
- add_
note_ in_ session - As
add_note, but a decoy write is attributed tosession_ref— a fresh ref per duress unlock yields the git-like per-session branch (ADR §10). Ignored for real-lane writes (real activity is never audited). - get_
retention_ mode - Read the decoy-audit retention policy (real-lane setting). Requires the real PIN; defaults to
RetentionMode::AutoArchivewhen never set. - is_
configured - Is the encrypted vault configured on disk?
- is_
keychain_ wrapped - Is the on-disk vault keychain-wrapped (T1.2)?
- list_
notes - Read the notes held in the lane the PIN opens. Nothing is readable without a valid PIN.
- real_
curate_ decoy_ add_ note - Real-session decoy curation (ADR §3.2). From a real unlock, write a note into the decoy lane without the decoy PIN — the real lane unwraps the decoy key from its one-way hierarchy. This is how the victim keeps the decoy lived-in and plausible so a coercer’s re-unlock shows believable content. Requires the real PIN; supplying the decoy PIN is rejected (the decoy cannot curate itself, and must not be able to detect that curation exists).
- resolve_
lane - Resolve which lane a PIN opens (or an error if it opens neither).
- review_
decoy_ activity - Review decoy activity from the real lane (ADR §3.1 / §10). Opens the real lane, unwraps the audit secret, decrypts every sealed decoy-session record, verifies chain integrity, and checks each previously-witnessed prefix against its head anchor (detecting forensic truncation/replace). Then advances the anchors for clean branches and persists them inside the real lane’s encrypted records. Requires the real PIN; the decoy PIN is rejected.
- set_
retention_ mode - Set the decoy-audit retention policy (ADR §8). Real-session only — the setting is stored in the real lane’s encrypted records so it is invisible and unreachable from a decoy session (which must never learn that auditing exists). Requires the real PIN.
- setup
- Create the two encrypted lanes with the production Argon2id KDF (memory-hard; ADR D1). Fails if a PIN is too weak, the PINs are equal, or the vault already exists.
- setup_
wrapped - Opt-in (off by default). Create the two encrypted lanes with an OS-keychain-held pepper mixed
into the KDF, so disk + PIN alone cannot open the vault. Returns the pepper as a hex recovery
code: the caller MUST have the user record it out-of-band — if the keychain entry is later lost
(reinstall / new machine), this code is the only way back in (see
unlock_with_recovery). - unlock_
with_ recovery - Recover access to a keychain-wrapped vault whose keychain entry is missing (new device, OS
reinstall) by supplying the hex recovery code from
setup_wrapped. On success the pepper is re-stored into this device’s keychain so subsequent unlocks are seamless again.