Documentation Index
Fetch the complete documentation index at: https://mintlify.com/openmls/openmls/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Add new members to a group by providing their KeyPackages. The operation creates a Commit message and Welcome message for the new members.
Adding Members
Basic Add
Add members with a path (updates your key material):
use openmls::prelude::*;
let key_packages: &[KeyPackage] = &[bob_key_package, charlie_key_package];
let (commit, welcome, group_info) = alice_group.add_members(
provider,
&signer,
key_packages,
)?;
// Send commit to existing members
// Send welcome to new members
alice_group.merge_pending_commit(provider)?;
Add Without Update
Add members without forcing a path:
let (commit, welcome, group_info) = alice_group.add_members_without_update(
provider,
&signer,
key_packages,
)?;
alice_group.merge_pending_commit(provider)?;
Using CommitBuilder
For more control over the commit:
let bundle = alice_group
.commit_builder()
.propose_adds(key_packages.iter().cloned())
.force_self_update(true)
.load_psks(provider.storage())?
.build(provider.rand(), provider.crypto(), &signer, |_| true)?
.stage_commit(provider)?;
let commit = bundle.commit();
let welcome = bundle.welcome().expect("expected welcome");
let group_info = bundle.group_info();
Method Signatures
add_members
Adds members with a commit path (includes key material update).
OpenMLS provider for crypto and storage
Signature key for authenticating the commit
KeyPackages of members to add (must not be empty)
Returns: Result<(MlsMessageOut, MlsMessageOut, Option<GroupInfo>), AddMembersError>
The tuple contains:
- Commit message (send to existing members)
- Welcome message (send to new members)
- GroupInfo (Some if
use_ratchet_tree_extension is enabled)
Location: openmls/src/group/mls_group/membership.rs:41
add_members_without_update
Adds members without forcing a path. A path is only included if pending proposals require it.
OpenMLS provider for crypto and storage
Signature key for authenticating the commit
KeyPackages of members to add
Returns: Result<(MlsMessageOut, MlsMessageOut, Option<GroupInfo>), AddMembersError>
Location: openmls/src/group/mls_group/membership.rs:118
swap_members
Replaces existing members with new members atomically.
OpenMLS provider for crypto and storage
Signature key for authenticating the commit
Indices of members to remove
KeyPackages of replacement members (must be same length)
Returns: Result<WelcomeCommitMessages, SwapMembersError>
Location: openmls/src/group/mls_group/membership.rs:63
Complete Example
Obtain KeyPackages
// Get KeyPackages from new members
// (they generate and publish these)
let bob_key_package: KeyPackage = /* from Bob */;
let charlie_key_package: KeyPackage = /* from Charlie */;
Add members to group
let (commit_msg, welcome_msg, group_info_option) =
alice_group.add_members(
provider,
&alice_signer,
&[bob_key_package, charlie_key_package],
)?;
Send messages
// Send commit to existing group members
delivery_service.send_to_group(alice_group.group_id(), commit_msg)?;
// Send welcome to new members (Bob and Charlie)
delivery_service.send_welcome(welcome_msg)?;
// Optionally send group_info
if let Some(group_info) = group_info_option {
delivery_service.publish_group_info(group_info)?;
}
Merge the commit
// Alice merges her own pending commit
alice_group.merge_pending_commit(provider)?;
New members join
// Bob receives and processes the Welcome
let bob_group = StagedWelcome::new_from_welcome(
provider,
&join_config,
welcome_msg.into_welcome()?,
ratchet_tree,
)?
.into_group(provider)?;
Swap Members Example
// Replace members at indices 2 and 3 with new members
let old_member_indices = &[
LeafNodeIndex::new(2),
LeafNodeIndex::new(3),
];
let new_key_packages = &[
new_member_1_kp,
new_member_2_kp,
];
let messages = alice_group.swap_members(
provider,
&signer,
old_member_indices,
new_key_packages,
)?;
let commit = messages.commit();
let welcome = messages.welcome();
// Send messages and merge
alice_group.merge_pending_commit(provider)?;
Error Handling
match alice_group.add_members(provider, &signer, key_packages) {
Ok((commit, welcome, group_info)) => {
// Success - send messages
}
Err(AddMembersError::EmptyInput(_)) => {
// key_packages slice was empty
}
Err(AddMembersError::GroupStateError(state_error)) => {
match state_error {
MlsGroupStateError::PendingCommit => {
// Must merge or clear pending commit first
}
MlsGroupStateError::UseAfterEviction => {
// Can't use group after being removed
}
}
}
Err(AddMembersError::CreateCommitError(commit_error)) => {
// Failed to create the commit
}
Err(e) => eprintln!("Error adding members: {}", e),
}
Advanced Usage
Custom Proposal Filtering
Control which proposals to include:
let bundle = alice_group
.commit_builder()
.propose_adds(key_packages.iter().cloned())
.consume_proposal_store(true) // Include pending proposals
.load_psks(provider.storage())?
.build(
provider.rand(),
provider.crypto(),
&signer,
|proposal| {
// Custom filter: only accept Add and Update proposals
matches!(
proposal.proposal(),
Proposal::Add(_) | Proposal::Update(_)
)
},
)?
.stage_commit(provider)?;
Add with Group Context Extensions
let bundle = alice_group
.commit_builder()
.propose_adds(key_packages.iter().cloned())
.propose_group_context_extension(
Extension::RequiredCapabilities(
RequiredCapabilitiesExtension::new(&[/* ... */])
)
)
.load_psks(provider.storage())?
.build(provider.rand(), provider.crypto(), &signer, |_| true)?
.stage_commit(provider)?;
Important Notes
Pending Commits: You cannot add members if there’s already a pending commit. Merge or clear it first.
Welcome Messages: New members need the Welcome message to join. Without it, they cannot decrypt group messages.
Path vs No Path: add_members() always includes a path (updating your leaf). add_members_without_update() only includes a path if required by other pending proposals.
Empty Input: Both methods return an error if the key_packages slice is empty.
Processing Add Commits
Existing members receive and process the commit:
// Bob processes Alice's add commit
let processed = bob_group.process_message(provider, commit_msg)?;
if let ProcessedMessageContent::StagedCommitMessage(staged_commit) =
processed.into_content()
{
// Inspect the commit
for add in staged_commit.add_proposals() {
println!("New member added");
}
// Merge the commit
bob_group.merge_staged_commit(provider, *staged_commit)?;
}
Next Steps