TypeScript implementation of the Marmot protocol - bringing end-to-end encrypted group messaging to Nostr using MLS (Messaging Layer Security).
Warning
This library is currently in Alpha and under heavy development. The API is subject to breaking changes without notice. It relies heavily on ts-mls for MLS cryptographic guarantees. Do not use in production yet.
This library provides the building blocks for creating secure, decentralized group chat applications on Nostr. It wraps ts-mls with Nostr-specific functionality, similar to how MDK wraps OpenMLS.
- 🔐 End-to-end encrypted group messaging using MLS protocol
- 🌐 Decentralized - groups operate across Nostr relays
- 🔑 Key package management - handle identity, publishing, rotation, and invitations
- 📦 Storage-agnostic - bring your own
GenericKeyValueStorebackend (LocalForage, IndexedDB, SQLite, in-memory, etc.) - 🔌 Network-agnostic - works with any Nostr client library
- 📱 Cross-platform - works in browsers and Node.js (v20+)
npm install @internet-privacy/marmot-ts
# or
pnpm add @internet-privacy/marmot-tsCurrently, marmot-ts supports the following Marmot Improvement Proposals (MIPs):
| MIP | Description | Status |
|---|---|---|
| MIP-00 | Introduction and Basic Operations | ✅ Supported |
| MIP-01 | Network Transport & Relay Communication | ✅ Supported |
| MIP-02 | Identities and Keys | ✅ Supported |
| MIP-03 | Group State & Memberships | ✅ Supported |
Comprehensive documentation is available in the documentation/ directory:
- Getting Started - A fast track to initializing the library.
- Architecture - High-level component overview and Nostr/MLS integration mapping.
- MarmotClient - Deep dive into the main entry point class, its sub-managers, and identity management.
- Bytes-First Storage - Explaining the storage-agnostic philosophy and group state hydration.
- Ingest Methods - Handling incoming messages and network input robustly.
- Examples - Concise snippets for group creation, invitations, sending messages, and more.
To begin using the client, you need an EventSigner (e.g. from applesauce-core), a NostrNetworkInterface implementation, and two GenericKeyValueStore backends — one for serialized group state bytes and one for key package metadata.
import { MarmotClient } from "@internet-privacy/marmot-ts";
import localforage from "localforage";
const client = new MarmotClient({
signer: yourNostrSigner,
// Any GenericKeyValueStore<SerializedClientState>. A LocalForage instance
// works directly because it already implements the getItem/setItem/keys API.
groupStateStore: localforage.createInstance({ name: "marmot-groups" }),
// Any GenericKeyValueStore<StoredKeyPackage> for key package metadata.
keyPackageStore: localforage.createInstance({ name: "marmot-keypackages" }),
// Your NostrNetworkInterface implementation (publish, request, subscription, getUserInboxRelays).
network: yourNetworkInterface,
// Optional: stable slot identifier for addressable (kind 30443) key packages.
clientId: "my-app-desktop",
});
const group = await client.groups.create("My Secret Group", {
description: "A private discussion",
relays: ["wss://relay.example.com"],
// Optional: add additional admins (the creator is always included automatically)
adminPubkeys: ["<other-admin-pubkey-hex>"],
// Optional: override MLS ciphersuite
ciphersuite: "MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519",
});See Getting Started and Examples for full usage instructions.
pnpm install # Install dependencies
pnpm build # Compile TypeScript
pnpm test # Run tests (watch mode)
pnpm format # Format code with Prettier