Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 32 additions & 28 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ lru = "0.18"
mockall = { version = "0.14" }
mockall_double = "0.3.1"
once_cell = "1.21.4"
openmls = { git = "https://github.com/xmtp/openmls", rev = "e125674303a715b00bf133ceba903862304eac83", default-features = false, features = [
openmls = { git = "https://github.com/xmtp/openmls", rev = "9cdd5e798cb9217ef246964885e87d371ea553b3", default-features = false, features = [
"extensions-draft-08",
] }
openmls_basic_credential = { git = "https://github.com/xmtp/openmls", rev = "e125674303a715b00bf133ceba903862304eac83" }
openmls_libcrux_crypto = { git = "https://github.com/xmtp/openmls", rev = "e125674303a715b00bf133ceba903862304eac83", features = [
openmls_basic_credential = { git = "https://github.com/xmtp/openmls", rev = "9cdd5e798cb9217ef246964885e87d371ea553b3" }
openmls_libcrux_crypto = { git = "https://github.com/xmtp/openmls", rev = "9cdd5e798cb9217ef246964885e87d371ea553b3", features = [
"extensions-draft-08",
] }
openmls_rust_crypto = { git = "https://github.com/xmtp/openmls", rev = "e125674303a715b00bf133ceba903862304eac83" }
openmls_traits = { git = "https://github.com/xmtp/openmls", rev = "e125674303a715b00bf133ceba903862304eac83", features = [
openmls_rust_crypto = { git = "https://github.com/xmtp/openmls", rev = "9cdd5e798cb9217ef246964885e87d371ea553b3" }
openmls_traits = { git = "https://github.com/xmtp/openmls", rev = "9cdd5e798cb9217ef246964885e87d371ea553b3", features = [
"extensions-draft-08",
] }
owo-colors = { version = "4.1" }
Expand Down
65 changes: 65 additions & 0 deletions bindings/node/src/client/external_invite.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use napi::bindgen_prelude::{Result, Uint8Array};
use napi_derive::napi;

use super::Client;
use crate::ErrorWrapper;
use crate::conversation::Conversation;

/// Output of [`Client::join_group_by_external_invite`].
///
/// Exposes the newly-joined group (via the `conversation` getter) and the
/// post-commit re-encrypted GroupInfo blob (`refreshedEncryptedGroupInfo`).
/// The application should upload `refreshedEncryptedGroupInfo` back to its
/// invite service under the same `groupIdHash` key the original blob used
/// (overwrite semantics) so the next joiner reads a GroupInfo at the new
/// epoch — otherwise their external commit would race a stale ratchet tree.
#[napi]
pub struct JoinGroupByExternalInviteOutput {
conversation: Conversation,
refreshed_encrypted_group_info: Vec<u8>,
}

#[napi]
impl JoinGroupByExternalInviteOutput {
#[napi(getter)]
pub fn conversation(&self) -> Conversation {
self.conversation.clone()
}

#[napi(getter)]
pub fn refreshed_encrypted_group_info(&self) -> Uint8Array {
self.refreshed_encrypted_group_info.as_slice().into()
}
}

#[napi]
impl Client {
/// Join a group via an external invite (atomic external commit).
///
/// `invitePayload` is the serialized `ExternalInvitePayload` proto carried
/// by the QR-code or link. `encryptedGroupInfo` is the encrypted blob the
/// application fetched from its invite service using the payload's
/// `groupIdHash` as the lookup key.
///
/// On success returns the joined [`Conversation`] together with a
/// post-commit `refreshedEncryptedGroupInfo` blob that the caller should
/// ship back to the invite service so the next joiner reads a GroupInfo
/// at the new epoch.
#[napi]
pub async fn join_group_by_external_invite(
&self,
invite_payload: Uint8Array,
encrypted_group_info: Uint8Array,
) -> Result<JoinGroupByExternalInviteOutput> {
let output = self
.inner_client
.join_group_by_external_invite(invite_payload.as_ref(), encrypted_group_info.as_ref())
.await
.map_err(ErrorWrapper::from)?;

Ok(JoinGroupByExternalInviteOutput {
conversation: output.group.into(),
refreshed_encrypted_group_info: output.refreshed_encrypted_group_info,
})
}
}
1 change: 1 addition & 0 deletions bindings/node/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use xmtp_mls::groups::MlsGroup;
pub mod backend;
mod consent_state;
pub mod create_client;
mod external_invite;
pub(crate) mod gateway_auth;
mod identity;
mod inbox_state;
Expand Down
Loading
Loading