pub struct MessagingClient { /* private fields */ }Implementations§
Source§impl MessagingClient
impl MessagingClient
Sourcepub async fn init(cfg: ClientConfig) -> Result<Arc<Self>>
pub async fn init(cfg: ClientConfig) -> Result<Arc<Self>>
Initialise. Creates a new local device if none is recorded in storage; otherwise rehydrates.
pub fn user_id(&self) -> UserId
pub fn device_id(&self) -> DeviceId
pub fn device_info(&self, now_ms: u64) -> DeviceInfo
Sourcepub async fn fresh_key_package(&self) -> Result<Vec<u8>>
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.
Sourcepub async fn create_conversation(
self: &Arc<Self>,
name: Option<String>,
now_ms: u64,
) -> Result<ConversationId>
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).
Sourcepub async fn join_conversation(
self: &Arc<Self>,
welcome_envelope: &MessageEnvelope,
now_ms: u64,
) -> Result<ConversationId>
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.
pub fn list_conversations(&self) -> Vec<ConversationMeta>
Sourcepub fn members(&self, conv_id: ConversationId) -> Vec<MemberInfo>
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.
Sourcepub async fn send(
&self,
conv_id: ConversationId,
plaintext: Vec<u8>,
now_ms: u64,
) -> Result<MessageEnvelope>
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.
Sourcepub async fn add_members(
&self,
conv_id: ConversationId,
entries: Vec<(DeviceId, Vec<u8>)>,
now_ms: u64,
) -> Result<()>
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.
Sourcepub async fn admit_device_to_chats(
&self,
new_device_id: DeviceId,
kps_per_chat: Vec<(ConversationId, Vec<u8>)>,
now_ms: u64,
) -> Result<Vec<AdmitChatOutcome>>
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 thedevice_binding_sigrecipient 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).
pub async fn remove_members( &self, conv_id: ConversationId, leaf_indexes: Vec<u32>, now_ms: u64, ) -> Result<()>
Sourcepub async fn process_envelope(
&self,
env: &MessageEnvelope,
now_ms: u64,
) -> Result<Option<IncomingMessage>>
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).
Sourcepub async fn sync_conversations(
&self,
now_ms: u64,
) -> Result<Vec<IncomingMessage>>
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.
Sourcepub 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>
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).
Sourcepub async fn consume_linking_ticket(
self: &Arc<Self>,
ticket: &LinkingTicket,
now_ms: u64,
) -> Result<()>
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.
Sourcepub fn export_conversation_state_snapshot(
&self,
conv_id: ConversationId,
now_ms: u64,
) -> Result<Zeroizing<Vec<u8>>>
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.
Sourcepub async fn import_state_snapshot(
self: &Arc<Self>,
snapshot_bytes: &[u8],
now_ms: u64,
) -> Result<ConversationId>
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.
Sourcepub fn export_conversation_secret(
&self,
conv_id: ConversationId,
label: &str,
context: &[u8],
length: usize,
) -> Result<Zeroizing<Vec<u8>>>
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.
Sourcepub async fn revoke_device(
&self,
device_id: DeviceId,
now_ms: u64,
) -> Result<Vec<MessageEnvelope>>
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§
Auto Trait Implementations§
impl !Freeze for MessagingClient
impl !RefUnwindSafe for MessagingClient
impl !UnwindSafe for MessagingClient
impl Send for MessagingClient
impl Sync for MessagingClient
impl Unpin for MessagingClient
impl UnsafeUnpin for MessagingClient
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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