Skip to main content

ping_mls_store/
backend.rs

1// `PathBuf` + `Zeroizing` are only referenced by the `Sqlite` variant, which is
2// native-only. Gate the imports the same way to avoid a wasm32 unused-imports lint.
3#[cfg(not(target_arch = "wasm32"))]
4use std::path::PathBuf;
5#[cfg(not(target_arch = "wasm32"))]
6use zeroize::Zeroizing;
7
8use std::sync::Arc;
9
10use crate::AsyncBlobStore;
11
12/// Where the persistent provider checkpoints `MemoryStorage` to. Selected at
13/// `MessagingClient::init` time via [`ping_core::ClientConfig::storage_backend`].
14#[derive(Debug, Clone, Default)]
15pub enum StorageBackend {
16    /// In-memory only. Default and what tests use; loses state on process exit.
17    /// Cold-start scenarios (iOS NSE, web Service Worker) MUST use [`Self::Sqlite`]
18    /// or [`Self::IndexedDb`] instead.
19    #[default]
20    Memory,
21
22    /// SQLite-backed; native targets only. The SDK owns the file at `path`; the
23    /// parent directory must exist. Pass `encryption_key = Some(...)` to enable
24    /// SQLCipher; absence means the file is unencrypted (tests, dev only).
25    #[cfg(not(target_arch = "wasm32"))]
26    Sqlite {
27        /// Absolute path. Host convention: `<app_support>/ping/<account_id>/mls.sqlite`
28        /// for per-account isolation (CR-18); the SDK doesn't enforce this — the host
29        /// picks the path.
30        path: PathBuf,
31        /// SQLCipher key. The SDK zeroes its copy on drop; the host is responsible
32        /// for sourcing this from the OS keyring (Keychain / Keystore / etc.).
33        encryption_key: Option<Zeroizing<[u8; 32]>>,
34    },
35
36    /// Host-supplied async blob storage. Available on every target.
37    ///
38    /// The provider snapshots the entire `MemoryStorage` HashMap into a
39    /// single CBOR blob and round-trips it through the
40    /// [`AsyncBlobStore`] the host implements. This is the universal
41    /// persistence path:
42    ///   * **WASM**: host wraps its IndexedDB layer (PingStorageWeb, which
43    ///     already AES-GCM-encrypts every row under a non-extractable
44    ///     wrap key kept inside the same IDB).
45    ///   * **iOS / macOS / Android**: host wraps the same `Storage` trait
46    ///     it already provides for conv metadata + cursors (typically
47    ///     Keychain-encrypted SQLite or AsyncStorage), so the OpenMLS
48    ///     snapshot sits under a reserved `("__mls", "snapshot")` slot
49    ///     alongside the host's other persisted KV.
50    ///
51    /// Why this replaces the wasm-only `IndexedDb` variant: any host
52    /// that already implements `Storage` (which every binding does)
53    /// gets persistence for free, without per-platform path management,
54    /// Keychain key plumbing, or SQLCipher dependency. The SQLite
55    /// backend remains available for hosts that want a separate file
56    /// (e.g. iOS NSE cold-start where the main app's Storage isn't
57    /// reachable from the extension).
58    AsyncBlob { blob_store: Arc<dyn AsyncBlobStore> },
59}