> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/xmtp/libxmtp/llms.txt
> Use this file to discover all available pages before exploring further.

# Metadata

> Group metadata options and management in LibXMTP

## 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.

```rust theme={null}
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

| Field                           | Type                                  | Description                        | Max Length |
| ------------------------------- | ------------------------------------- | ---------------------------------- | ---------- |
| `name`                          | `Option<String>`                      | Group display name                 | 100 chars  |
| `image_url_square`              | `Option<String>`                      | URL for group image                | 2048 chars |
| `description`                   | `Option<String>`                      | Group description                  | 1000 chars |
| `message_disappearing_settings` | `Option<MessageDisappearingSettings>` | Disappearing message configuration | N/A        |
| `app_data`                      | `Option<String>`                      | Application-specific data          | 8192 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

```rust theme={null}
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.

```rust theme={null}
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.

```rust theme={null}
pub struct GroupMetadata {
    pub conversation_type: ConversationType,
    pub creator_inbox_id: String,
    pub dm_members: Option<DmMembers<InboxId>>,
    pub oneshot_message: Option<OneshotMessage>,
}
```

### Fields

| Field               | Type                         | Description                            |
| ------------------- | ---------------------------- | -------------------------------------- |
| `conversation_type` | `ConversationType`           | Type of conversation (Group, DM, etc.) |
| `creator_inbox_id`  | `String`                     | Inbox ID of the group creator          |
| `dm_members`        | `Option<DmMembers<InboxId>>` | DM member information (DMs only)       |
| `oneshot_message`   | `Option<OneshotMessage>`     | Optional oneshot message               |

### Methods

#### new

```rust theme={null}
pub fn new(
    conversation_type: ConversationType,
    creator_inbox_id: String,
    dm_members: Option<DmMembers<InboxId>>,
    oneshot_message: Option<OneshotMessage>,
) -> Self
```

### Conversions

```rust theme={null}
impl TryFrom<GroupMetadata> for Vec<u8>
impl TryFrom<&Vec<u8>> for GroupMetadata
impl TryFrom<GroupMetadataProto> for GroupMetadata
impl TryFrom<&Extensions> for GroupMetadata
```

### Helper Function

```rust theme={null}
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.

```rust theme={null}
pub struct GroupMutableMetadata {
    pub attributes: HashMap<String, String>,
    pub admin_list: Vec<String>,
    pub super_admin_list: Vec<String>,
}
```

### Fields

| Field              | Type                      | Description                   |
| ------------------ | ------------------------- | ----------------------------- |
| `attributes`       | `HashMap<String, String>` | Key-value metadata attributes |
| `admin_list`       | `Vec<String>`             | List of admin inbox IDs       |
| `super_admin_list` | `Vec<String>`             | List of super admin inbox IDs |

### Methods

#### new

```rust theme={null}
pub fn new(
    attributes: HashMap<String, String>,
    admin_list: Vec<String>,
    super_admin_list: Vec<String>,
) -> Self
```

#### new\_default

```rust theme={null}
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

```rust theme={null}
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

```rust theme={null}
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

```rust theme={null}
pub fn is_admin(&self, inbox_id: &String) -> bool
```

Checks if an inbox ID is an admin.

#### is\_super\_admin

```rust theme={null}
pub fn is_super_admin(&self, inbox_id: &String) -> bool
```

Checks if an inbox ID is a super admin.

#### commit\_log\_signer

```rust theme={null}
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

```rust theme={null}
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

```rust theme={null}
pub fn find_mutable_metadata_extension(
    extensions: &Extensions,
) -> Option<&Vec<u8>>
```

Searches for the mutable metadata extension in MLS Extensions.

#### extract\_group\_mutable\_metadata

```rust theme={null}
pub fn extract_group_mutable_metadata(
    group: &OpenMlsGroup,
) -> Result<GroupMutableMetadata, GroupMutableMetadataError>
```

Extracts mutable metadata from an OpenMLS group.

## MetadataField

Enum representing supported metadata fields.

```rust theme={null}
pub enum MetadataField {
    GroupName,
    Description,
    GroupImageUrlSquare,
    MessageDisappearFromNS,
    MessageDisappearInNS,
    MinimumSupportedProtocolVersion,
    CommitLogSigner,
    AppData,
}
```

### Methods

#### as\_str

```rust theme={null}
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

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

## MessageDisappearingSettings

Configuration for disappearing messages.

```rust theme={null}
pub struct MessageDisappearingSettings {
    pub from_ns: i64,
    pub in_ns: i64,
}
```

### Fields

| Field     | Type  | Description                                                               |
| --------- | ----- | ------------------------------------------------------------------------- |
| `from_ns` | `i64` | Timestamp (nanoseconds) from when messages should be tracked for deletion |
| `in_ns`   | `i64` | Duration (nanoseconds) after which tracked messages will be deleted       |

### Methods

#### new

```rust theme={null}
pub fn new(from_ns: i64, in_ns: i64) -> Self
```

#### is\_enabled

```rust theme={null}
pub fn is_enabled(&self) -> bool
```

Returns `true` if both `from_ns` and `in_ns` are greater than 0.

### Default Implementation

```rust theme={null}
impl Default for MessageDisappearingSettings {
    fn default() -> Self {
        Self { from_ns: 0, in_ns: 0 }
    }
}
```

## DmMembers

Represents the two members of a DM conversation.

```rust theme={null}
pub struct DmMembers<Id: AsRef<str>> {
    pub member_one_inbox_id: Id,
    pub member_two_inbox_id: Id,
}
```

### Methods

#### as\_ref

```rust theme={null}
impl DmMembers<String> {
    pub fn as_ref(&'a self) -> DmMembers<&'a str>
}
```

### Display Implementation

```rust theme={null}
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

```rust theme={null}
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

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

### GroupMutableMetadataError

```rust theme={null}
pub enum GroupMutableMetadataError {
    Serialization(prost::EncodeError),
    Deserialization(prost::DecodeError),
    MissingExtension,
    NonMutableExtensionUpdate,
    TooManyUpdates,
    NoUpdates,
    MissingMetadataField,
}
```

## Usage Examples

### Creating a Group with Custom Metadata

```rust theme={null}
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

```rust theme={null}
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

```rust theme={null}
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`:

```rust theme={null}
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`
