Skip to content
A pixel-art marmot in uniform holding a clipboard next to a stone tablet with checkmarks

Protocol Constants & Concepts ​

Protocol Constants ​

Event Kinds ​

Marmot uses specific Nostr event kinds for different purposes:

typescript
import {
  ADDRESSABLE_KEY_PACKAGE_KIND, // 30443
  WELCOME_EVENT_KIND, // 444
  GROUP_EVENT_KIND, // 445
  NIP65_RELAY_LIST_KIND, // 10002
  INBOX_RELAY_LIST_KIND, // 10050
} from "@internet-privacy/marmot-ts";
  • 30443 (ADDRESSABLE_KEY_PACKAGE_KIND): Addressable key package advertisement events
  • 444 (WELCOME_EVENT_KIND): Welcome messages for new members (wrapped in NIP-59 gift wraps)
  • 445 (GROUP_EVENT_KIND): Group messages (commits, proposals, application messages)
  • 10002 (NIP65_RELAY_LIST_KIND): NIP-65 relay list; Marmot discovers an account's key-package relays here (there is no dedicated key-package relay list)
  • 10050 (INBOX_RELAY_LIST_KIND): Inbox relay list; welcomes are gift-wrapped to a recipient's inbox relays

Extension Types ​

MLS extensions used by Marmot:

typescript
import { LAST_RESORT_EXTENSION_TYPE } from "@internet-privacy/marmot-ts"; // 0x000a
  • 0x000a (LAST_RESORT_EXTENSION_TYPE): Marks key packages as reusable

In Marmot v2, group metadata is no longer carried in a single 0xf2ee extension — it lives in the app-component dictionary described below.

Protocol Versions ​

Key package events use MLS protocol version tag value "1.0". The exported MLS_VERSIONS name is a TypeScript type alias for supported values.

App Components (group state) ​

Marmot v2 stores group state as versioned app components inside the MLS app_data_dictionary GroupContext extension (0x0006, draft-ietf-mls-extensions-09), replacing the v1 MarmotGroupData monolith. Each component has a stable id and its own binary codec; the dictionary is cryptographically bound to the group state and mutated through app_data_update proposals (0x0008).

Group components ​

IdComponentHolds
0x8001group.profile.v1name, description
0x8003admin-policy.v1admin Nostr pubkeys
0x8004transport.nostr.routing.v1nostr group id + relays
0x8005message-retention.v1retention window (seconds)
0x8007group.avatar-url.v1avatar URL
0x8008group.encrypted-media.v1blob-store policy for media

Reading group state ​

getMarmotGroupView projects the recognized components into one object:

typescript
import { getMarmotGroupView } from "@internet-privacy/marmot-ts";

const view = getMarmotGroupView(clientState);
view?.name; // "Developer Chat"
view?.adminPubkeys; // ["admin-pubkey-hex"]
view?.relays; // ["wss://relay.example.com"]
view?.nostrGroupId; // Uint8Array(32)
view?.avatarUrl; // "https://..." | undefined
view?.encryptedMedia; // EncryptedMediaPolicyV1 | undefined

Individual components can be read with the typed getters (getGroupProfile, getAdminPolicy, getNostrRouting, getGroupAvatarUrl, getEncryptedMediaPolicy, ...) and built with the matching entry builders (groupProfileEntry, adminPolicyEntry, nostrRoutingEntry, ...).

Required capabilities ​

New groups declare a required_capabilities (0x0003) extension covering the Marmot baseline — app_data_dictionary (0x0006) and the app_data_update (0x0008) / self_remove (0x000a) proposals — so MLS refuses to add a member whose KeyPackage does not advertise them. Every new group also requires the account identity proof app component (0x8009) in its app_components list.

Specification Reference ​

See the darkmatter (Marmot v2) spec for the complete app-component and capability-negotiation model.