Skip to main content

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.

StagedWelcome allows inspection of a Welcome message before creating an MlsGroup. This enables validation of the group’s members, context, and sender before committing to join.

Overview

When joining a group via a Welcome message, StagedWelcome provides an intermediate state where you can:
  • Inspect the group’s current members
  • Check the group context and extensions
  • Identify who sent the Welcome
  • Export secrets before fully joining
  • Convert to an MlsGroup when ready

Creating a staged welcome

new_from_welcome

Creates a StagedWelcome from a Welcome message and optional ratchet tree.
provider
&Provider
OpenMLS provider for cryptographic operations and storage
mls_group_config
&MlsGroupJoinConfig
Configuration for joining the group
welcome
Welcome
The Welcome message to process
ratchet_tree
Option<RatchetTreeIn>
Optional ratchet tree. Required if not included in the Welcome’s ratchet tree extension.
Result
Result<StagedWelcome, WelcomeError>
Returns a staged welcome for inspection or an error if processing fails
let staged_welcome = StagedWelcome::new_from_welcome(
    provider,
    &mls_group_config,
    welcome,
    Some(ratchet_tree),
)?;

// Inspect before joining
for member in staged_welcome.members() {
    println!("Member: {:?}", member.credential);
}

// Join when ready
let group = staged_welcome.into_group(provider)?;
This function consumes the key material for decrypting the Welcome message. Even if you don’t convert to an MlsGroup, the key package will be consumed.

build_from_welcome

Creates a builder for more advanced Welcome processing.
provider
&Provider
OpenMLS provider
mls_group_config
&MlsGroupJoinConfig
Configuration for joining
welcome
Welcome
The Welcome message
Result
Result<JoinBuilder, WelcomeError>
Returns a builder for customizing the join process
let staged_welcome = StagedWelcome::build_from_welcome(provider, &config, welcome)?
    .with_ratchet_tree(ratchet_tree)
    .skip_lifetime_validation()
    .build()?;

Inspecting the welcome

welcome_sender

Returns the leaf node of the member who sent the Welcome.
Result
Result<&LeafNode, LibraryError>
Reference to the sender’s leaf node
let sender = staged_welcome.welcome_sender()?;
println!("Welcome from: {:?}", sender.credential());

welcome_sender_index

Returns the leaf index of the Welcome sender.
LeafNodeIndex
LeafNodeIndex
The sender’s leaf index in the tree
let sender_index = staged_welcome.welcome_sender_index();

group_context

Returns the group context from the Welcome.
GroupContext
&GroupContext
Reference to the group context containing epoch, group ID, extensions, etc.
let context = staged_welcome.group_context();
println!("Group ID: {:?}", context.group_id());
println!("Epoch: {}", context.epoch().as_u64());
println!("Ciphersuite: {:?}", context.ciphersuite());

members

Returns an iterator over all current members of the group.
Iterator
impl Iterator<Item = Member>
Iterator over group members
for member in staged_welcome.members() {
    println!("Member {}: {:?}", 
        member.index.u32(), 
        member.credential
    );
}

Converting to a group

into_group

Consumes the StagedWelcome and creates an MlsGroup.
provider
&Provider
OpenMLS provider for storage and crypto operations
Result
Result<MlsGroup, WelcomeError>
Returns the fully initialized MlsGroup
let group = staged_welcome.into_group(provider)?;

// Group is now ready for normal operations
let (commit, welcome, group_info) = group.add_members(
    provider,
    signer,
    &[key_package],
)?;
This operation:
  • Stores the group state to persistent storage
  • Stores encryption keypairs
  • Initializes the message secrets
  • Sets up the resumption PSK store

Export secrets

export_secret

Exports a secret from the epoch that will be joined.
crypto
&CryptoProvider
Cryptographic provider
label
&str
Label for the exported secret
context
&[u8]
Context bytes for derivation
key_length
usize
Desired key length in bytes (max: u16::MAX)
Result
Result<Vec<u8>, ExportSecretError>
Returns the exported secret or an error if the key length is too long
let secret = staged_welcome.export_secret(
    provider.crypto(),
    "my-app-secret",
    b"context",
    32,
)?;
Secrets can be exported before converting to an MlsGroup. This is useful for deriving application-level keys tied to the group epoch.

Validation workflow

Typical workflow for validating a Welcome before joining:
// 1. Stage the welcome
let staged_welcome = StagedWelcome::new_from_welcome(
    provider,
    &config,
    welcome,
    ratchet_tree,
)?;

// 2. Validate the sender
let sender = staged_welcome.welcome_sender()?;
if !is_trusted_sender(sender.credential()) {
    return Err("Untrusted Welcome sender");
}

// 3. Check group properties
let context = staged_welcome.group_context();
if !is_allowed_group(context.group_id()) {
    return Err("Not allowed to join this group");
}

// 4. Inspect members
let member_count = staged_welcome.members().count();
if member_count > MAX_GROUP_SIZE {
    return Err("Group too large");
}

// 5. Verify all members
for member in staged_welcome.members() {
    if !is_valid_member(&member.credential) {
        return Err("Invalid member in group");
    }
}

// 6. Export any needed secrets
let app_secret = staged_welcome.export_secret(
    provider.crypto(),
    "app-level-key",
    b"",
    32,
)?;

// 7. Join the group
let group = staged_welcome.into_group(provider)?;

Builder pattern

For advanced use cases, use the builder pattern:
let staged_welcome = StagedWelcome::build_from_welcome(
    provider,
    &config,
    welcome,
)?
.with_ratchet_tree(ratchet_tree)      // Provide ratchet tree
.skip_lifetime_validation()            // Skip leaf lifetime checks
.replace_old_group()                   // Replace existing group with same ID
.build()?;

Builder methods

with_ratchet_tree

Provides the ratchet tree for group initialization.
ratchet_tree
RatchetTreeIn
The ratchet tree to use

skip_lifetime_validation

Skips validation of leaf node lifetimes in the ratchet tree. Only leaf nodes that have never been updated have a lifetime.

replace_old_group

Allows replacing an existing group with the same group ID. By default, attempting to join a group that already exists in storage returns an error.

processed_welcome

Returns a reference to the ProcessedWelcome for inspection before building.
ProcessedWelcome
&ProcessedWelcome
Reference to the processed welcome containing unverified values

Error handling

StagedWelcome creation can fail for several reasons:
match StagedWelcome::new_from_welcome(provider, &config, welcome, ratchet_tree) {
    Ok(staged) => { /* Success */ },
    Err(WelcomeError::NoMatchingKeyPackage) => {
        // No key package found for this Welcome
    },
    Err(WelcomeError::MissingRatchetTree) => {
        // Ratchet tree required but not provided
    },
    Err(WelcomeError::ConfirmationTagMismatch) => {
        // Welcome failed validation
    },
    Err(WelcomeError::GroupAlreadyExists) => {
        // Group with this ID already exists in storage
    },
    Err(e) => {
        // Other errors
    }
}

Use cases

Conditional group joining

let staged_welcome = StagedWelcome::new_from_welcome(
    provider, &config, welcome, ratchet_tree
)?;

// Only join if sender is authorized
if authorized_senders.contains(staged_welcome.welcome_sender()?.credential()) {
    let group = staged_welcome.into_group(provider)?;
} else {
    log::warn!("Rejected Welcome from unauthorized sender");
}

Group metadata collection

let staged_welcome = StagedWelcome::new_from_welcome(
    provider, &config, welcome, ratchet_tree
)?;

let metadata = GroupMetadata {
    group_id: staged_welcome.group_context().group_id().clone(),
    member_count: staged_welcome.members().count(),
    epoch: staged_welcome.group_context().epoch().as_u64(),
    sender: staged_welcome.welcome_sender()?.credential().clone(),
};

store_metadata(metadata);
let group = staged_welcome.into_group(provider)?;

Pre-join key derivation

let staged_welcome = StagedWelcome::new_from_welcome(
    provider, &config, welcome, ratchet_tree
)?;

// Derive application keys before joining
let encryption_key = staged_welcome.export_secret(
    provider.crypto(), "app-encryption", b"", 32
)?;
let signing_key = staged_welcome.export_secret(
    provider.crypto(), "app-signing", b"", 64
)?;

// Join and use derived keys
let group = staged_welcome.into_group(provider)?;