Skip to main content

Overview

LibXMTP groups support two types of metadata:
  1. GroupMetadata - Immutable metadata set at group creation
  2. GroupMutableMetadata - Mutable metadata that can be updated based on permissions
Both are stored as MLS Unknown Group Context Extensions.

GroupMetadataOptions

Options for configuring group metadata at creation time.
pub struct GroupMetadataOptions {
    pub name: Option<String>,
    pub image_url_square: Option<String>,
    pub description: Option<String>,
    pub message_disappearing_settings: Option<MessageDisappearingSettings>,
    pub app_data: Option<String>,
}

Fields

FieldTypeDescriptionMax Length
nameOption<String>Group display name100 chars
image_url_squareOption<String>URL for group image2048 chars
descriptionOption<String>Group description1000 chars
message_disappearing_settingsOption<MessageDisappearingSettings>Disappearing message configurationN/A
app_dataOption<String>Application-specific data8192 chars

Defaults

If not specified, fields use these defaults (from xmtp_configuration):
  • name: DEFAULT_GROUP_NAME
  • description: DEFAULT_GROUP_DESCRIPTION
  • image_url_square: DEFAULT_GROUP_IMAGE_URL_SQUARE
  • app_data: Empty string

Implementation

impl Default for GroupMetadataOptions {
    fn default() -> Self {
        Self {
            name: None,
            image_url_square: None,
            description: None,
            message_disappearing_settings: None,
            app_data: None,
        }
    }
}

DMMetadataOptions

Options for configuring DM (direct message) metadata.
pub struct DMMetadataOptions {
    pub message_disappearing_settings: Option<MessageDisappearingSettings>,
}
Note: DMs have fewer configurable options since name/description are derived from participants.

GroupMetadata (Immutable)

Immutable metadata created at group creation time.
pub struct GroupMetadata {
    pub conversation_type: ConversationType,
    pub creator_inbox_id: String,
    pub dm_members: Option<DmMembers<InboxId>>,
    pub oneshot_message: Option<OneshotMessage>,
}

Fields

FieldTypeDescription
conversation_typeConversationTypeType of conversation (Group, DM, etc.)
creator_inbox_idStringInbox ID of the group creator
dm_membersOption<DmMembers<InboxId>>DM member information (DMs only)
oneshot_messageOption<OneshotMessage>Optional oneshot message

Methods

new

pub fn new(
    conversation_type: ConversationType,
    creator_inbox_id: String,
    dm_members: Option<DmMembers<InboxId>>,
    oneshot_message: Option<OneshotMessage>,
) -> Self

Conversions

impl TryFrom<GroupMetadata> for Vec<u8>
impl TryFrom<&Vec<u8>> for GroupMetadata
impl TryFrom<GroupMetadataProto> for GroupMetadata
impl TryFrom<&Extensions> for GroupMetadata

Helper Function

pub fn extract_group_metadata(
    extensions: &Extensions,
) -> Result<GroupMetadata, GroupMetadataError>
Extracts immutable metadata from MLS extensions.

GroupMutableMetadata

Mutable metadata that can be updated according to permission policies.
pub struct GroupMutableMetadata {
    pub attributes: HashMap<String, String>,
    pub admin_list: Vec<String>,
    pub super_admin_list: Vec<String>,
}

Fields

FieldTypeDescription
attributesHashMap<String, String>Key-value metadata attributes
admin_listVec<String>List of admin inbox IDs
super_admin_listVec<String>List of super admin inbox IDs

Methods

new

pub fn new(
    attributes: HashMap<String, String>,
    admin_list: Vec<String>,
    super_admin_list: Vec<String>,
) -> Self

new_default

pub fn new_default(
    creator_inbox_id: String,
    commit_log_signer: Option<Secret>,
    opts: GroupMetadataOptions,
) -> Self
Creates default mutable metadata for a new group. Behavior:
  • Creator is added as super admin
  • Attributes populated from opts
  • Commit log signer stored if provided

new_dm_default

pub fn new_dm_default(
    _creator_inbox_id: String,
    _dm_target_inbox_id: &str,
    commit_log_signer: Option<Secret>,
    opts: DMMetadataOptions,
) -> Self
Creates default mutable metadata for a DM. Behavior:
  • No admins or super admins (not needed for DMs)
  • Minimal attributes

supported_fields

pub fn supported_fields() -> Vec<MetadataField>
Returns metadata fields that receive default permission policies. Supported Fields:
  • GroupName
  • Description
  • GroupImageUrlSquare
  • MessageDisappearFromNS
  • MessageDisappearInNS
  • MinimumSupportedProtocolVersion
  • AppData

is_admin

pub fn is_admin(&self, inbox_id: &String) -> bool
Checks if an inbox ID is an admin.

is_super_admin

pub fn is_super_admin(&self, inbox_id: &String) -> bool
Checks if an inbox ID is a super admin.

commit_log_signer

pub fn commit_log_signer(&self) -> Option<Secret>
Retrieves the commit log signer secret from attributes. Returns: None if field is not present or hex decoding fails.

Conversions

impl TryFrom<GroupMutableMetadata> for Vec<u8>
impl TryFrom<&Vec<u8>> for GroupMutableMetadata
impl TryFrom<GroupMutableMetadataProto> for GroupMutableMetadata
impl TryFrom<&Extensions> for GroupMutableMetadata
impl TryFrom<&OpenMlsGroup> for GroupMutableMetadata

Helper Functions

find_mutable_metadata_extension

pub fn find_mutable_metadata_extension(
    extensions: &Extensions,
) -> Option<&Vec<u8>>
Searches for the mutable metadata extension in MLS Extensions.

extract_group_mutable_metadata

pub fn extract_group_mutable_metadata(
    group: &OpenMlsGroup,
) -> Result<GroupMutableMetadata, GroupMutableMetadataError>
Extracts mutable metadata from an OpenMLS group.

MetadataField

Enum representing supported metadata fields.
pub enum MetadataField {
    GroupName,
    Description,
    GroupImageUrlSquare,
    MessageDisappearFromNS,
    MessageDisappearInNS,
    MinimumSupportedProtocolVersion,
    CommitLogSigner,
    AppData,
}

Methods

as_str

pub const fn as_str(&self) -> &'static str
Returns the string representation used as a key in the attributes map. Mappings:
  • GroupName"group_name"
  • Description"description"
  • GroupImageUrlSquare"group_image_url_square"
  • MessageDisappearFromNS"message_disappear_from_ns"
  • MessageDisappearInNS"message_disappear_in_ns"
  • MinimumSupportedProtocolVersion"minimum_supported_protocol_version"
  • CommitLogSigner"_commit_log_signer" (super admin prefix)
  • AppData"app_data"
Note: CommitLogSigner uses the _ prefix to make it super-admin-only.

Display Implementation

impl fmt::Display for MetadataField {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

MessageDisappearingSettings

Configuration for disappearing messages.
pub struct MessageDisappearingSettings {
    pub from_ns: i64,
    pub in_ns: i64,
}

Fields

FieldTypeDescription
from_nsi64Timestamp (nanoseconds) from when messages should be tracked for deletion
in_nsi64Duration (nanoseconds) after which tracked messages will be deleted

Methods

new

pub fn new(from_ns: i64, in_ns: i64) -> Self

is_enabled

pub fn is_enabled(&self) -> bool
Returns true if both from_ns and in_ns are greater than 0.

Default Implementation

impl Default for MessageDisappearingSettings {
    fn default() -> Self {
        Self { from_ns: 0, in_ns: 0 }
    }
}

DmMembers

Represents the two members of a DM conversation.
pub struct DmMembers<Id: AsRef<str>> {
    pub member_one_inbox_id: Id,
    pub member_two_inbox_id: Id,
}

Methods

as_ref

impl DmMembers<String> {
    pub fn as_ref(&'a self) -> DmMembers<&'a str>
}

Display Implementation

impl<Id: AsRef<str>> Display for DmMembers<Id> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // Formats as "dm:{sorted_inbox_id_1}:{sorted_inbox_id_2}"
        // IDs are lowercased and sorted for consistent ordering
    }
}
Example: DmMembers { member_one_inbox_id: "Alice", member_two_inbox_id: "Bob" } becomes "dm:alice:bob"

Conversions

impl<Id: AsRef<str>> From<DmMembers<Id>> for DmMembersProto
impl<Id: AsRef<str>> From<DmMembers<Id>> for String
impl TryFrom<DmMembersProto> for DmMembers<InboxId>

Error Handling

GroupMetadataError

pub enum GroupMetadataError {
    Serialization(prost::EncodeError),
    Deserialization(prost::DecodeError),
    InvalidConversationType,
    MissingExtension,
    InvalidDmMembers,
    MissingDmMember,
    Conversion(xmtp_proto::ConversionError),
}

GroupMutableMetadataError

pub enum GroupMutableMetadataError {
    Serialization(prost::EncodeError),
    Deserialization(prost::DecodeError),
    MissingExtension,
    NonMutableExtensionUpdate,
    TooManyUpdates,
    NoUpdates,
    MissingMetadataField,
}

Usage Examples

Creating a Group with Custom Metadata

use xmtp_mls_common::group::GroupMetadataOptions;
use xmtp_mls_common::group_mutable_metadata::MessageDisappearingSettings;

let opts = GroupMetadataOptions {
    name: Some("My Awesome Group".to_string()),
    description: Some("A group for awesome people".to_string()),
    image_url_square: Some("https://example.com/image.png".to_string()),
    message_disappearing_settings: Some(MessageDisappearingSettings::new(
        1_000_000_000,  // from_ns: start time
        86_400_000_000_000,  // in_ns: 24 hours
    )),
    app_data: Some(r#"{"custom":"data"}"#.to_string()),
};

let group = MlsGroup::create_and_insert(
    context,
    ConversationType::Group,
    PolicySet::default(),
    opts,
    None,
)?;

Extracting Metadata from a Group

use xmtp_mls_common::group_metadata::extract_group_metadata;
use xmtp_mls_common::group_mutable_metadata::extract_group_mutable_metadata;

// Extract immutable metadata
let metadata = extract_group_metadata(mls_group.extensions())?;
println!("Creator: {}", metadata.creator_inbox_id);

// Extract mutable metadata
let mutable = extract_group_mutable_metadata(&mls_group)?;
if let Some(name) = mutable.attributes.get("group_name") {
    println!("Group name: {}", name);
}

Checking Admin Status

let mutable = extract_group_mutable_metadata(&mls_group)?;
let my_inbox_id = context.inbox_id().to_string();

if mutable.is_super_admin(&my_inbox_id) {
    println!("I'm a super admin!");
} else if mutable.is_admin(&my_inbox_id) {
    println!("I'm a regular admin");
} else {
    println!("I'm a regular member");
}

Constants

Defined in crates/xmtp_mls/src/groups/mod.rs:
const MAX_GROUP_DESCRIPTION_LENGTH: usize = 1000;
const MAX_GROUP_NAME_LENGTH: usize = 100;
const MAX_GROUP_IMAGE_URL_LENGTH: usize = 2048;
const MAX_APP_DATA_LENGTH: usize = 8192;

Source References

  • GroupMetadataOptions: crates/xmtp_mls_common/src/group.rs:3
  • DMMetadataOptions: crates/xmtp_mls_common/src/group.rs:12
  • GroupMetadata: crates/xmtp_mls_common/src/group_metadata.rs:36
  • GroupMutableMetadata: crates/xmtp_mls_common/src/group_mutable_metadata.rs:99
  • MetadataField: crates/xmtp_mls_common/src/group_mutable_metadata.rs:42
  • MessageDisappearingSettings: crates/xmtp_mls_common/src/group_mutable_metadata.rs:83
  • DmMembers: crates/xmtp_mls_common/src/group_metadata.rs:115

Build docs developers (and LLMs) love