review mandatory
This document defines Group Events (kind: 445) that enable secure group communication in Marmot Protocol. Group Events include both control messages (Proposals and Commits) and application messages (regular chat content).
Group Events enable secure group communication through encrypted control messages (Proposal and Commit) and application messages (chat, reactions) with metadata protection.
Group Events use ephemeral keypairs and encrypted content to protect member identities and prevent group size analysis by observers.
{
"id": "ghi789...",
"kind": 445,
"created_at": 1693876700,
"pubkey": "03b2c4d6e8f0a1b3c5d7e9f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1d3e5f7a9b1c3",
"content": "a1b2c3d4e5f6...",
"tags": [
["h", "group_id_from_nostr_extension"]
],
"sig": "506070809..."
}content: SerializedMLSMessageencrypted with NIP-44pubkey: Ephemeral public key (different for each Group Event)htag: Group ID from the Marmot Group Data Extension
- Get group secret: Use the MLS
exporter_secretfrom the current group epoch - Generate keypair: Create a Nostr keypair using the
exporter_secretas the private key - Apply NIP-44: Encrypt the MLSMessage using the standard NIP-44 scheme. However, instead of using the sender and receiver's keys to derive a
conversation_key, the NIP-44 encryption is done using a Nostr keypair generated from the MLSexporter_secretto calculate theconversation_keyvalue. Use the raw 32-byteexporter_secretas the secp256k1 private key (the sender key), derive the corresponding public key (used as the receiver key), and then proceed with the standard NIP-44 scheme to encrypt and decrypt messages. - Publish: Publish the event to relays using a completely separate ephemeral keypair. This is NOT the same keypair you created in step 2.
Group modifications use a two-phase approach for safety and coordination:
- Proposal Phase: Members suggest changes
- Commit Phase: Changes are applied (admins for most changes, any member for their own key updates)
Who can propose: Any group member can create proposals
What proposals do:
- Suggest adding new members
- Propose removing members
- Recommend changing group settings
- Request key rotations
Proposal lifecycle:
- Created by any member
- Stored for review
- Included (or rejected) in future Commits
Who can commit: Group admins can create and send any Commit. Non-admin members can ONLY create self-update Commits.
What commits do:
- Apply one or more pending proposals
- Update the group's cryptographic state
- Move the group to a new "epoch" (version)
Self-update Commits:
A self-update Commit is one where the committer updates only their own key material (their LeafNode). This is essential for maintaining forward secrecy and post-compromise security (PCS), as members need to periodically rotate their signing keys.
- Any member (admin or non-admin) MAY create a self-update Commit
- Self-update Commits MUST contain only an Update proposal for the committer's own LeafNode
- Self-update Commits MUST NOT include any other proposals (Add, Remove, GroupContextExtensions, etc.)
Security requirements:
- For self-update Commits: Clients MUST verify the Commit contains ONLY an Update proposal for the sender's own LeafNode
- For all other Commits: Sender MUST be listed in
admin_pubkeys(see MIP-01) - Clients MUST verify admin status before processing any non-self-update Commit
- Invalid Commits from non-admins (other than valid self-updates) MUST be rejected
Admin Control: This design ensures only authorized admins can make structural changes to groups (membership, settings), while allowing all members to maintain their own key hygiene through self-updates and participate in governance through proposals.
While MLS handles most out-of-order messages gracefully, Commit message ordering is critical for proper group state progression. In Nostr's decentralized environment, multiple admins might send competing Commits simultaneously.
Clients sending Commits MUST:
- Send the Group Event: Publish the Commit as a Group Event to relays
- Wait for acknowledgment: Don't apply the Commit locally until at least one relay confirms receipt
- Then apply locally: Only then update your own group state
- Then send any associated Welcomes: If this Commit adds new members, Welcome messages may only be sent AFTER relay acknowledgment (see MIP-02 Timing Requirements)
When a Commit adds new members to an existing group, the timing of Welcome message delivery is critical. If a Welcome is sent before the Commit is confirmed by relays, the new member may activate into an epoch that existing members haven't reached yet, causing a state fork where messages cannot be decrypted across the fork boundary.
See MIP-02 for the complete timing requirements and state fork prevention guidance.
Exception - Initial Group Creation: The very first Commit that creates a group MUST NOT be sent to relays. This initial Commit establishes epoch 0 and exists only locally until members are invited. Sending it would unnecessarily expose metadata about group creation timing and the creating admin's activity. The first Commit that gets published to relays is the one that adds the first member.
Note: This exception intentionally departs from the MLS specification's general recommendation to confirm all commits before applying them locally. In a traditional MLS deployment with a central delivery service, confirming commits prevents state divergence. However, for the initial group creation in Marmot, there are no other members who could create a conflicting state, and publishing the creation commit would leak unnecessary metadata to relays. Privacy benefits outweigh the coordination guarantees that relay confirmation provides for subsequent commits.
When receiving multiple Commits for the same epoch, clients MUST apply exactly one using this priority:
- Timestamp priority: Choose the Commit with the earliest
created_attimestamp - ID tiebreaker: If timestamps are identical, choose the Commit with the lexicographically smallest
id - Discard others: Reject all other competing Commits
Clients SHOULD retain previous group states temporarily to enable recovery from forked states.
Example scenario:
- Admin A and Admin B both send Commits at timestamp 1693876800
- Admin A's Commit has ID "aaa123...", Admin B's has "bbb456..."
- All clients apply Admin A's Commit (lexicographically smaller ID)
- Admin B's Commit is discarded
Application messages are the regular content shared within groups - chat messages, reactions, media sharing, and any other member-to-member communication.
Application messages use standard Nostr event formats inside the encrypted MLS envelope:
- Chat messages:
kind: 9events for regular text messages - Reactions:
kind: 7events for emoji reactions to other messages - Other content: Any appropriate Nostr event kind
This approach provides the best of both worlds:
- MLS security: End-to-end encryption and forward secrecy
- Nostr features: Rich event types, reactions, threading, and extensibility
- Familiar handling: Clients can reuse existing Nostr event processing logic
Identity verification:
- Inner events MUST use the sender's Nostr identity key in the
pubkeyfield - Clients MUST verify the MLS sender matches the inner event's pubkey
Prevent leaks:
- Inner events MUST remain unsigned (no
sigfield) - MUST NOT include
htags or other group identifiers - This ensures leaked events cannot be published to public relays
- Create: Member creates a
kind: 9chat message event (unsigned) - Wrap: Event is serialized and wrapped in an MLS Application message
- Encrypt: MLS message is encrypted and sent as a Group Event
- Decrypt: Recipients decrypt and deserialize to get the original Nostr event
- Store: Clients can store and display the event like any other Nostr content
- Group Events (
kind: 445): Encrypted container for all group communication - Proposal Messages: Member-initiated suggestions for group changes
- Commit Messages: Admin-authorized group state changes (except self-updates, which any member can perform)
- Application Messages: Regular content using Nostr event formats
MUST implement:
- Admin verification for non-self-update Commit messages with race condition handling
- Self-update Commit validation (must contain only sender's own Update proposal)
- Key generation from
exporter_secretwith NIP-44 encryption - Identity verification for inner events (unsigned to prevent leaks)
- Ephemeral keypairs (never reuse) for privacy protection
To maintain group security and minimize stale state:
- Remove inactive members: Clients SHOULD regularly suggest to group admins that inactive members be removed. This improves forward secrecy by excluding members who may have compromised devices and reduces stale token/key accumulation.
- Rotate signing keys: Clients SHOULD regularly rotate the user's signing key in each group to strengthen post-compromise security.
- Prune stale data: When members are removed via MLS Remove operations, all associated data (tokens, cached keys) SHOULD be cleaned up promptly.