Skip to main content

qualia_client_core/wellfair/api/
sanctuary_vault.rs

1//! Vault v2 decoy audit + OS-keychain wrapping
2
3use super::*;
4
5impl WebizenHostApi {
6    // --- Vault v2 (S6): per-session decoy audit, real→decoy curation, real-lane audit review ---
7
8    /// Add a note, attributing a **decoy** (duress) write to `session_ref` — a fresh ref per duress
9    /// unlock yields the git-like per-session branch in the audit DAG (ADR §10). Real-lane writes
10    /// ignore `session_ref` (real activity is never audited). The host should mint one `session_ref`
11    /// per unlock (e.g. a UUID) and reuse it for every write in that session.
12    #[cfg(not(target_arch = "wasm32"))]
13    pub fn add_sanctuary_vault_note_in_session(
14        &self,
15        pin: &str,
16        body: &str,
17        session_ref: &str,
18    ) -> Result<super::super::sanctuary_vault::SanctuaryLane, String> {
19        super::super::sanctuary_vault::add_note_in_session(
20            &self.storage_root,
21            pin,
22            body,
23            Self::now_unix() as u32,
24            session_ref,
25        )
26    }
27
28    /// **Curate the decoy from a real session (ADR §3.2).** Write a plausible note into the decoy
29    /// lane *without* the decoy PIN, so a coercer's re-unlock shows fresh, believable content.
30    /// Requires the **real** PIN; the decoy/wrong PIN is rejected.
31    #[cfg(not(target_arch = "wasm32"))]
32    pub fn curate_sanctuary_decoy_note(&self, real_pin: &str, body: &str) -> Result<(), String> {
33        super::super::sanctuary_vault::real_curate_decoy_add_note(
34            &self.storage_root,
35            real_pin,
36            body,
37            Self::now_unix() as u32,
38        )
39    }
40
41    /// **Review decoy activity from the real lane (ADR §3.1 / §10).** Decrypts every sealed
42    /// decoy-session record, verifies chain integrity + each witnessed-prefix head anchor, advances
43    /// the anchors, and returns the decrypted actions with an integrity verdict. Requires the
44    /// **real** PIN. `session_count` is a proxy for "number of attackers", never a hard head-count.
45    #[cfg(not(target_arch = "wasm32"))]
46    pub fn review_sanctuary_decoy_activity(
47        &self,
48        real_pin: &str,
49    ) -> Result<super::super::sanctuary_vault::DecoyActivityReport, String> {
50        super::super::sanctuary_vault::review_decoy_activity(&self.storage_root, real_pin)
51    }
52
53    /// Read the decoy-audit retention policy (ADR §8). **Real-session only** — requires the real PIN;
54    /// the setting is invisible/unreachable from a decoy session. Defaults to auto-archive.
55    #[cfg(not(target_arch = "wasm32"))]
56    pub fn get_sanctuary_decoy_retention_mode(
57        &self,
58        real_pin: &str,
59    ) -> Result<qualia_core_db::crypto::sanctuary_audit_dag::RetentionMode, String> {
60        super::super::sanctuary_vault::get_retention_mode(&self.storage_root, real_pin)
61    }
62
63    /// Set the decoy-audit retention policy (ADR §8). **Real-session only** — requires the real PIN.
64    #[cfg(not(target_arch = "wasm32"))]
65    pub fn set_sanctuary_decoy_retention_mode(
66        &self,
67        real_pin: &str,
68        mode: qualia_core_db::crypto::sanctuary_audit_dag::RetentionMode,
69    ) -> Result<(), String> {
70        super::super::sanctuary_vault::set_retention_mode(&self.storage_root, real_pin, mode)
71    }
72
73    // --- T1.2: OS-keychain vault wrapping (opt-in, off by default; recovery-gated) ---
74
75    /// Is the on-disk Sanctuary vault keychain-wrapped (bound to an OS-keychain pepper)?
76    #[cfg(not(target_arch = "wasm32"))]
77    pub fn sanctuary_vault_is_keychain_wrapped(&self) -> bool {
78        super::super::sanctuary_vault::is_keychain_wrapped(&self.storage_root)
79    }
80
81    /// Opt-in: create the Sanctuary vault with an OS-keychain-held pepper so disk + PIN alone can't
82    /// open it. Returns the hex **recovery code** the user MUST record — losing the keychain entry
83    /// otherwise loses the vault. The ordinary [`Self::setup_sanctuary_vault`] path stays unwrapped.
84    #[cfg(not(target_arch = "wasm32"))]
85    pub fn setup_sanctuary_vault_wrapped(
86        &self,
87        real_pin: &str,
88        decoy_pin: &str,
89    ) -> Result<String, String> {
90        super::super::sanctuary_vault::setup_wrapped(&self.storage_root, real_pin, decoy_pin)
91    }
92
93    /// Recover a keychain-wrapped vault on a device whose keychain entry is missing, using the
94    /// recovery code from [`Self::setup_sanctuary_vault_wrapped`]. Re-seats the pepper on success.
95    #[cfg(not(target_arch = "wasm32"))]
96    pub fn sanctuary_vault_unlock_with_recovery(
97        &self,
98        pin: &str,
99        recovery_code_hex: &str,
100    ) -> Result<super::super::sanctuary_vault::SanctuaryLane, String> {
101        super::super::sanctuary_vault::unlock_with_recovery(
102            &self.storage_root,
103            pin,
104            recovery_code_hex,
105        )
106    }
107}