Adding members to a group

Immediate operation

Members can be added to the group atomically with the .add_members() function. The application needs to fetch the corresponding key packages from every new member from the Delivery Service first.

    let (mls_message_out, welcome, group_info) = alice_group
        .add_members(provider, &alice_signature_keys, &[bob_key_package])
        .expect("Could not add members.");

The function returns the tuple (MlsMessageOut, Welcome). The MlsMessageOut contains a Commit message that needs to be fanned out to existing group members. The Welcome message must be sent to the newly added members.

Proposal

Members can also be added as a proposal (without the corresponding Commit message) by using the .propose_add_member() function:

    let (mls_message_out, _proposal_ref) = alice_group
        .propose_add_member(provider, &alice_signature_keys, &bob_key_package)
        .expect("Could not create proposal to add Bob");

In this case, the function returns an MlsMessageOut that needs to be fanned out to existing group members.

External proposal

Parties outside the group can also make proposals to add themselves to the group with an external proposal. Since those proposals are crafted by outsiders, they are always plaintext messages.

    let proposal = JoinProposal::new(
        bob_key_package,
        alice_group.group_id().clone(),
        alice_group.epoch(),
        &bob_signature_keys,
    )
    .expect("Could not create external Add proposal");

It is then up to the group members to validate the proposal and commit it. Note that in this scenario it is up to the application to define a proper authorization policy to grant the sender.

    let alice_processed_message = alice_group
        .process_message(
            provider,
            proposal
                .into_protocol_message()
                .expect("Unexpected message type."),
        )
        .expect("Could not process message.");
    match alice_processed_message.into_content() {
        ProcessedMessageContent::ExternalJoinProposalMessage(proposal) => {
            alice_group.store_pending_proposal(*proposal);
            let (_commit, welcome, _group_info) = alice_group
                .commit_to_pending_proposals(provider, &alice_signature_keys)
                .expect("Could not commit");
            assert_eq!(alice_group.members().count(), 1);
            alice_group
                .merge_pending_commit(provider)
                .expect("Could not merge commit");
            assert_eq!(alice_group.members().count(), 2);

            let bob_group = MlsGroup::new_from_welcome(
                provider,
                mls_group_create_config.join_config(),
                welcome
                    .unwrap()
                    .into_welcome()
                    .expect("Unexpected message type."),
                None,
            )
            .expect("Bob could not join the group");
            assert_eq!(bob_group.members().count(), 2);
        }
        _ => unreachable!(),
    }