Skip to main content

StorageBackend

Enum StorageBackend 

Source
pub enum StorageBackend {
    Memory,
    Sqlite {
        path: PathBuf,
        encryption_key: Option<Zeroizing<[u8; 32]>>,
    },
    SqliteReadOnly {
        path: PathBuf,
        encryption_key: Option<Zeroizing<[u8; 32]>>,
    },
    AsyncBlob {
        blob_store: Arc<dyn AsyncBlobStore>,
    },
    AsyncBlobReadOnly {
        blob_store: Arc<dyn AsyncBlobStore>,
    },
}
Expand description

[CR-4] re-export so hosts can build ClientConfig { storage_backend: … } without pulling ping_mls_store in directly. AsyncBlobStore is the async single-blob trait WASM hosts implement to back StorageBackend::IndexedDb; BlobFuture is the matching future-type helper. Where the persistent provider checkpoints MemoryStorage to. Selected at MessagingClient::init time via [ping_core::ClientConfig::storage_backend].

Variants§

§

Memory

In-memory only. Default and what tests use; loses state on process exit. Cold-start scenarios (iOS NSE, web Service Worker) MUST use Self::Sqlite or [Self::IndexedDb] instead.

§

Sqlite

SQLite-backed; native targets only. The SDK owns the file at path; the parent directory must exist. Pass encryption_key = Some(...) to enable SQLCipher; absence means the file is unencrypted (tests, dev only).

Fields

§path: PathBuf

Absolute path. Host convention: <app_support>/ping/<account_id>/mls.sqlite for per-account isolation (CR-18); the SDK doesn’t enforce this — the host picks the path.

§encryption_key: Option<Zeroizing<[u8; 32]>>

SQLCipher key. The SDK zeroes its copy on drop; the host is responsible for sourcing this from the OS keyring (Keychain / Keystore / etc.).

§

SqliteReadOnly

SQLite-backed, opened READ-ONLY and never written back; native only.

Exists for one job: letting a SECOND process (the iOS Notification Service Extension) decrypt an inbound message for a lock-screen preview while the main app remains the sole writer.

That restriction is not caution, it is the only sound arrangement given how this crate persists. Self::Sqlite checkpoints the ENTIRE MemoryStorage map as one CBOR blob in a single row, so two writers do not merge — the second one to flush replaces every mutation the first made, silently discarding epochs, ratchet state, and freshly-stored key material. Read-only removes that failure mode by construction: a reader takes a consistent WAL snapshot, decrypts from it, and drops everything. Nothing it touches can outlive the process, so nothing it does can be observed by the writer.

The cost is that the ratchet advance is thrown away, so the main app decrypts the same message again from its own state. That is correct and intended — the two are independent readers of the same snapshot, and neither can desynchronise the other.

[PersistentMlsProvider::checkpoint] is a NO-OP against this backend rather than an error, so shared code paths that flush opportunistically stay usable. Pair it with a read-only host Storage and [ping_core::MessagingClient::open_read_only], which refuses the state-creating paths (LocalDevice minting, DeviceGroup creation) that would otherwise write through a channel this backend does not cover.

Fields

§path: PathBuf

Absolute path to a file another process owns. Must already exist — unlike Self::Sqlite, this variant never creates one, because a created file would mean the writer’s real store was not found and silently decrypting nothing is worse than failing loudly.

§encryption_key: Option<Zeroizing<[u8; 32]>>

SQLCipher key. Same value the writing process uses; on iOS both read it from the shared Keychain access group.

§

AsyncBlob

Host-supplied async blob storage. Available on every target.

The provider snapshots the entire MemoryStorage HashMap into a single CBOR blob and round-trips it through the AsyncBlobStore the host implements. This is the universal persistence path:

  • WASM: host wraps its IndexedDB layer (PingStorageWeb, which already AES-GCM-encrypts every row under a non-extractable wrap key kept inside the same IDB).
  • iOS / macOS / Android: host wraps the same Storage trait it already provides for conv metadata + cursors (typically Keychain-encrypted SQLite or AsyncStorage), so the OpenMLS snapshot sits under a reserved ("__mls", "snapshot") slot alongside the host’s other persisted KV.

Why this replaces the wasm-only IndexedDb variant: any host that already implements Storage (which every binding does) gets persistence for free, without per-platform path management, Keychain key plumbing, or SQLCipher dependency. The SQLite backend remains available for hosts that want a separate file (e.g. iOS NSE cold-start where the main app’s Storage isn’t reachable from the extension).

Fields

§blob_store: Arc<dyn AsyncBlobStore>
§

AsyncBlobReadOnly

Self::AsyncBlob read-only — the universal-target counterpart to Self::SqliteReadOnly, for the same job on a platform with no SQLite.

The snapshot is read through the host’s blob store at open and never written back, so a second context (a web Service Worker decrypting a push for its notification, the mirror of the iOS NSE) can read the tab’s state without racing it. Same reasoning, same guarantee: the whole-state blob means two writers replace rather than merge, so the reader must not be one.

This variant is what keeps [ping_core::MessagingClient::open_read_only] a genuinely cross-platform API rather than a native-only one — on WASM it is the only backend that can satisfy it.

Fields

§blob_store: Arc<dyn AsyncBlobStore>

Trait Implementations§

Source§

impl Clone for StorageBackend

Source§

fn clone(&self) -> StorageBackend

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StorageBackend

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for StorageBackend

Source§

fn default() -> StorageBackend

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Classify for T

Source§

type Classified = T

Source§

fn classify(self) -> T

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Declassify for T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more