Skip to main content

MessagingClient

Struct MessagingClient 

Source
pub struct MessagingClient { /* private fields */ }

Implementations§

Source§

impl MessagingClient

Source

pub async fn init(cfg: ClientConfig) -> Result<Arc<Self>>

Initialise. Creates a new local device if none is recorded in storage; otherwise rehydrates.

Source

pub fn user_id(&self) -> UserId

Source

pub fn device_id(&self) -> DeviceId

Source

pub fn device_info(&self, now_ms: u64) -> DeviceInfo

Source

pub async fn fresh_key_package(&self) -> Result<Vec<u8>>

Generate a fresh KeyPackage to publish to the directory. Hosts call this when registering a device or topping up the directory.

build() writes the private init + encryption keys into the storage provider’s working set, but ON ITS OWN that write is NOT durable: on the WASM/AsyncBlob backend the working set only reaches IndexedDB at the next checkpoint_async, so a page reload before the next state-changing op loses the private keys while the PUBLIC KeyPackage has already been published. Any Welcome later bound to that KeyPackage then fails with “No matching key package was found in the key store” (breaking calls and every invite to this device). So we checkpoint HERE, before returning the bytes the host will publish — the published KeyPackage is durable the instant it leaves this function. Hence async.

Source

pub async fn create_conversation( self: &Arc<Self>, name: Option<String>, now_ms: u64, ) -> Result<ConversationId>

Create a new conversation owned by this client (and seeded with a single member: this device).

Source

pub async fn join_conversation( self: &Arc<Self>, welcome_envelope: &MessageEnvelope, now_ms: u64, ) -> Result<ConversationId>

Join via a Welcome bundled in a MessageEnvelope of kind Welcome.

Source

pub fn list_conversations(&self) -> Vec<ConversationMeta>

Source

pub fn members(&self, conv_id: ConversationId) -> Vec<MemberInfo>

Member roster for a conversation, recovered locally from the MLS group’s leaf credentials. Empty if the conversation is unknown to this client. Lets any device (including one that just joined via a linking Welcome) resolve a 1:1 peer’s UserId without the out-of-band ping.profile re-send.

Source

pub async fn send( &self, conv_id: ConversationId, plaintext: Vec<u8>, now_ms: u64, ) -> Result<MessageEnvelope>

Send an application message. Returns once the envelope has been handed to the transport.

Source

pub async fn add_members( &self, conv_id: ConversationId, entries: Vec<(DeviceId, Vec<u8>)>, now_ms: u64, ) -> Result<()>

Add members. The Commit goes on the wire; the Welcome should be delivered to the new devices’ inboxes (the host transport implements that — typically as a separate addressed envelope).

[CR-2] Each entry is (DeviceId, KeyPackage_bytes). The host typically gets the device_id from the directory at the same time it gets the KeyPackage; we use it to record a per-conversation device_id → leaf_index map so Self::revoke_device can later locate the leaf without a fresh directory lookup. The SDK does not cryptographically verify the host’s device-id claim — that’s a directory policy concern.

Source

pub async fn admit_device_to_chats( &self, new_device_id: DeviceId, kps_per_chat: Vec<(ConversationId, Vec<u8>)>, now_ms: u64, ) -> Result<Vec<AdmitChatOutcome>>

Admits new_device_id to every conversation in kps_per_chat via the standard MLS add_members flow — one Commit + one Welcome per chat. This is the SDK-side replacement for the host’s previous per-chat reconciler loop after device linking; centralising it here means iOS/Android/web hosts all share the orchestration and the transport’s Welcome-recipient priming is automatic.

Inputs:

  • new_device_id: the device being admitted (matches the device_binding_sig recipient in the linking ticket).
  • kps_per_chat: one freshly-claimed KeyPackage per chat. The host claims these via the auth-layer’s per-account KP pool (GET /v1/devices/{accountId}) AFTER the new device’s bootstrap has uploaded its KP batch.
  • now_ms: wall-clock used to stamp HLCs on the emitted envelopes.

Per-chat failures (unknown conversation, MLS error, transport error, etc.) are CAPTURED in the returned vec rather than short-circuiting the whole call — losing one chat shouldn’t strand the new device on every other chat. The caller decides whether to retry the failed entries (e.g. with a fresh KP).

Source

pub async fn remove_members( &self, conv_id: ConversationId, leaf_indexes: Vec<u32>, now_ms: u64, ) -> Result<()>

Source

pub async fn process_envelope( &self, env: &MessageEnvelope, now_ms: u64, ) -> Result<Option<IncomingMessage>>

Process an inbound envelope coming from the transport’s subscribe callback or a sync pull. Returns Some for application traffic, None for handshake messages (already merged).

Source

pub async fn sync_conversations( &self, now_ms: u64, ) -> Result<Vec<IncomingMessage>>

Catch-up sync: pull missing events for every open conversation since its cursor. Returns the list of newly-decrypted application messages, in apply order.

Source

pub async fn build_linking_ticket( self: &Arc<Self>, new_device_id: DeviceId, new_device_kp: Vec<u8>, last_app_events: Vec<(ConversationId, Vec<u8>)>, now_ms: u64, ) -> Result<LinkingTicket>

Build a LinkingTicket for a new device. The caller obtains new_device_kp from the new device (e.g., via QR-encoded handshake) and is responsible for sealing the returned ticket against the new device’s ephemeral X25519 pubkey before transmission via [ping_link::seal_ticket].

[CR-13] last_app_events is a host-supplied list of (conversation_id, app_event_bytes) for the new device’s “what you missed” UI. The SDK adds its own metas + (currently- empty) per-conversation MLS state and bundles everything into [device::CatchupSnapshot], CBOR-encoded into the ticket’s catchup_snapshot field. Pass an empty Vec to suppress catchup data (the new device sees an empty conversation list until normal sync runs).

Source

pub async fn consume_linking_ticket( self: &Arc<Self>, ticket: &LinkingTicket, now_ms: u64, ) -> Result<()>

Apply a received linking ticket. Joins the user’s DeviceGroup; the catch-up snapshot (if any) is decrypted by the host using the standard per-conversation channel afterwards.

Source

pub fn export_conversation_state_snapshot( &self, conv_id: ConversationId, now_ms: u64, ) -> Result<Zeroizing<Vec<u8>>>

[CR-7] Export the MLS state snapshot for one open conversation.

Thin pass-through to Conversation::export_state_snapshot. Returned bytes are wrapped in Zeroizing because they contain past epoch secrets.

Source

pub async fn import_state_snapshot( self: &Arc<Self>, snapshot_bytes: &[u8], now_ms: u64, ) -> Result<ConversationId>

[CR-7] Import a GroupStateSnapshot produced by another device’s Conversation::export_state_snapshot.

Replays the snapshot’s entries into this client’s OpenMLS provider, then reconstructs the Conversation handle via MlsGroup::load. After return, the conversation is in list_conversations() and send/process_envelope work against it normally.

Scope. This is for the same-user hand-off (linking, recovery). The snapshot exposes the exporter’s view of past epoch secrets for the target group; only call this when the receiving device has been authenticated to the same user identity (mnemonic, QR-handshake). Cross-user history transfer uses HPKE-sealed AppEvent re-shares (umbrella §15.6), not this method.

Sanity. Refuses snapshots whose group_id doesn’t match the bytes the receiver intends to claim — guards against host bugs that shuffle snapshots between groups. Refuses mismatched OpenMLS storage versions outright; no silent forward/back compatibility.

Source

pub fn export_conversation_secret( &self, conv_id: ConversationId, label: &str, context: &[u8], length: usize, ) -> Result<Zeroizing<Vec<u8>>>

Export a derived secret from one conversation’s MLS exporter ([CR-8]).

Thin pass-through to Conversation::export_secret. See that method’s doc comment for the contract on label, context, length validation, and zeroization. The returned Zeroizing<Vec<u8>> is automatically wiped when dropped.

Source

pub async fn revoke_device( &self, device_id: DeviceId, now_ms: u64, ) -> Result<Vec<MessageEnvelope>>

Revoke a device by removing its leaf from every conversation where we know its position ([CR-2]).

Returns one Commit envelope per conversation the device was a leaf in. The host broadcasts each envelope to the affected conversation; the SDK has also already handed them to the transport via transport.send (idempotent broadcast is the host’s call).

Scope. The SDK can only resolve leaves it recorded itself — either when it admitted the device via Self::add_members or when this device joined as the target via Welcome. For peer-admitted devices the leaf index isn’t locally known; those conversations are silently skipped. The host can fall back to remove_members(leaf_index) directly using a transport-side directory lookup if it needs to revoke from those conversations too. See docs/architecture/multi-device.md §Device removal for the broader flow.

Conversations with no entry for device_id produce no envelope; an empty Vec return is a valid outcome (e.g. the device was already revoked, or was never added by this client).

Trait Implementations§

Source§

impl Debug for MessagingClient

Source§

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

Formats the value using the given formatter. 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> 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, 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