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
Remove members from a group by specifying their leaf indices. The operation creates a Commit message that existing members must process.
Removing Members
Basic Removal
Remove one or more members:
use openmls::prelude::*;
let members_to_remove = &[
LeafNodeIndex::new(2),
LeafNodeIndex::new(5),
];
let (commit, welcome_option, group_info) = alice_group.remove_members(
provider,
&signer,
members_to_remove,
)?;
// Send commit to remaining members
alice_group.merge_pending_commit(provider)?;
Using CommitBuilder
For more control:
let bundle = alice_group
.commit_builder()
.propose_removals(members_to_remove.iter().cloned())
.load_psks(provider.storage())?
.build(provider.rand(), provider.crypto(), &signer, |_| true)?
.stage_commit(provider)?;
let commit = bundle.commit();
let welcome = bundle.welcome(); // Some if there were pending Add proposals
Method Signatures
remove_members
OpenMLS provider for crypto and storage
Signature key for authenticating the commit
Leaf indices of members to remove (must not be empty)
Returns: Result<(MlsMessageOut, Option<MlsMessageOut>, Option<GroupInfo>), RemoveMembersError>
The tuple contains:
- Commit message (send to remaining members)
- Welcome message (Some if there were pending Add proposals)
- GroupInfo (Some if
use_ratchet_tree_extension is enabled)
Location: openmls/src/group/mls_group/membership.rs:186
Finding Members to Remove
By Credential
// Find member by their credential
let member_credential: Credential = /* ... */;
if let Some(leaf_index) = alice_group.member_leaf_index(&member_credential) {
alice_group.remove_members(provider, &signer, &[leaf_index])?;
} else {
println!("Member not found in group");
}
List All Members
// Iterate over all members
for member in alice_group.members() {
println!("Member at index {}: {:?}",
member.index,
member.credential);
// Remove if needed
if should_remove(&member) {
alice_group.remove_members(provider, &signer, &[member.index])?;
break;
}
}
Get Member by Index
let leaf_index = LeafNodeIndex::new(3);
if let Some(member) = alice_group.member_at(leaf_index) {
println!("Found member: {:?}", member.credential);
alice_group.remove_members(provider, &signer, &[leaf_index])?;
}
Complete Example
Identify member to remove
// Get Bob's leaf index
let bob_credential: Credential = /* Bob's credential */;
let bob_index = alice_group
.member_leaf_index(&bob_credential)
.expect("Bob not in group");
Remove the member
let (commit, welcome, group_info) = alice_group.remove_members(
provider,
&alice_signer,
&[bob_index],
)?;
Send commit message
// Send to all remaining members (including Bob)
delivery_service.send_to_group(
alice_group.group_id(),
commit,
)?;
// Send welcome if there were pending adds
if let Some(welcome_msg) = welcome {
delivery_service.send_welcome(welcome_msg)?;
}
Merge the commit
alice_group.merge_pending_commit(provider)?;
Process removal (other members)
// Bob processes the commit
let processed = bob_group.process_message(provider, commit)?;
if let ProcessedMessageContent::StagedCommitMessage(staged) =
processed.into_content()
{
// Check if we were removed
if staged.self_removed() {
println!("We were removed from the group");
// bob_group is now inactive
}
bob_group.merge_staged_commit(provider, *staged)?;
}
Remove Operations
The RemoveOperation enum helps classify removal types:
use openmls::prelude::*;
// When processing a commit with remove proposals
for remove in staged_commit.remove_proposals() {
let operation = RemoveOperation::new(remove.clone(), &group)?;
match operation {
RemoveOperation::WeLeft => {
println!("We requested to leave and it was committed");
}
RemoveOperation::WeWereRemovedBy(sender) => {
println!("We were removed by: {:?}", sender);
}
RemoveOperation::TheyLeft(index) => {
println!("Member {} left", index);
}
RemoveOperation::TheyWereRemovedBy((index, sender)) => {
println!("Member {} was removed by {:?}", index, sender);
}
RemoveOperation::WeRemovedThem(index) => {
println!("We removed member {}", index);
}
}
}
Location: openmls/src/group/mls_group/membership.rs:350
Error Handling
match alice_group.remove_members(provider, &signer, members) {
Ok((commit, welcome, group_info)) => {
// Success
}
Err(RemoveMembersError::EmptyInput(_)) => {
// members slice was empty
}
Err(RemoveMembersError::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(RemoveMembersError::CreateCommitError(commit_error)) => {
// Failed to create commit
}
Err(e) => eprintln!("Error removing members: {}", e),
}
Advanced Usage
Propose Removal (Two-Step)
Create a Remove proposal without committing:
// Step 1: Propose removal
let (proposal_msg, proposal_ref) = alice_group.propose_remove_member(
provider,
&signer,
member_index,
)?;
// Send proposal to group
delivery_service.send_to_group(alice_group.group_id(), proposal_msg)?;
// Step 2: Later, commit pending proposals
let (commit, welcome, group_info) = alice_group
.commit_to_pending_proposals(provider, &signer)?;
Remove with Additional Proposals
// Remove members and update group context in one commit
let bundle = alice_group
.commit_builder()
.propose_removals(members_to_remove.iter().cloned())
.propose_group_context_extension(/* extension */)
.load_psks(provider.storage())?
.build(provider.rand(), provider.crypto(), &signer, |_| true)?
.stage_commit(provider)?;
External Remove Proposal
Allowed external entities can propose removals:
// External sender creates remove proposal
let external_proposal = /* ExternalProposal */;
// Group members process it
let processed = alice_group.process_message(provider, external_proposal)?;
if let ProcessedMessageContent::ProposalMessage(proposal) =
processed.into_content()
{
// Store the proposal
alice_group.store_pending_proposal(
provider.storage(),
*proposal,
)?;
// Later commit it
let (commit, _, _) = alice_group
.commit_to_pending_proposals(provider, &signer)?;
}
Important Notes
Self-Removal: To remove yourself, use leave_group() instead. It creates a Remove proposal that another member must commit.
Empty Input: remove_members() returns an error if the members slice is empty.
Welcome Messages: The Welcome in the return value is Some only if there were pending Add proposals in the proposal store.
Removed Member Processing: Removed members can still process the commit that removes them. After merging, their group becomes inactive.
Detecting Your Removal
When processing commits, check if you were removed:
let processed = group.process_message(provider, message)?;
if let ProcessedMessageContent::StagedCommitMessage(staged_commit) =
processed.into_content()
{
if staged_commit.self_removed() {
println!("We were removed from the group");
// After merging, the group becomes inactive
group.merge_staged_commit(provider, *staged_commit)?;
assert!(!group.is_active());
} else {
group.merge_staged_commit(provider, *staged_commit)?;
}
}
Next Steps