MLS on Nostr: encrypted group chat without a server

NIP-EE brings the MLS protocol to Nostr relays — forward secrecy, post-compromise security, and logarithmic group scaling on infrastructure that was never designed for any of it.

Where NIP-17 stops

NIP-17 was a real upgrade for #nostr DMs. Gift-wrapped messages, hidden sender identity, randomized timestamps, deniable inner events. If you read my previous article on NIP-17, you know the metadata protections are solid. Relay operators can’t tell who’s messaging whom, and that matters.

But NIP-17 has a gap that no amount of wrapping can fix: the encryption key between any two users never changes. Same ECDH shared secret, every message, forever. If your private key gets compromised tomorrow, every DM you’ve sent or received since you created your identity is readable. Past, present, future. There’s no mechanism to rotate, no way to recover. The compromise is permanent until you burn your identity and start over.

For groups, it’s worse. Each message has to be individually encrypted and gift-wrapped for every recipient. Ten people in a group means ten encryption operations per message. A hundred people means a hundred. The spec acknowledges this doesn’t scale past roughly a hundred participants.

NIP-17 optimized for the right thing given Nostr’s architecture. But it left two properties on the table that serious encrypted messaging requires: forward secrecy and post-compromise security. The closing paragraph of the NIP-44 spec itself says that if you need forward secrecy, use dedicated encrypted messaging software.

NIP-EE is the attempt to bring those guarantees to #nostr without giving up the relay model.

What MLS actually is

MLS : Messaging Layer Security : is RFC 9420, published by the IETF in 2023. It’s not a Nostr invention. It’s a protocol designed by cryptographers from Wire, Cisco, Mozilla, and others specifically to solve the group encryption scaling problem.

The core idea is a data structure called a ratchet tree. Picture a binary tree where each group member occupies a leaf node. Every internal node holds a key pair. When a member wants to update the group’s encryption keys, they generate fresh key material at their leaf and propagate it up their path to the root. At each level, they encrypt the new path secret so that members in the sibling subtree can decrypt it.

The result: updating the group key costs O(log N) encryption operations instead of O(N). A thousand-member group needs about ten operations, not a thousand.

MLS divides the group’s history into epochs. Each epoch has its own set of encryption keys derived from an epoch secret. When someone issues a Commit (an update, a member add, a member remove), the group transitions to a new epoch with fresh key material. Once the old epoch’s secrets are deleted, messages from that epoch can’t be decrypted even if someone later compromises a member’s key. That’s forward secrecy.

Post-compromise security works the other way. If a member’s key material is compromised, the group can heal. When any member issues a Commit with fresh entropy, the ratchet tree refreshes. The compromised key material becomes irrelevant to future epochs. The attacker loses access without anyone needing to burn their identity.

These aren’t theoretical properties. Wire shipped MLS to general availability in April 2025. Discord built their DAVE protocol for voice and video E2EE on top of MLS. Cisco has been running it in Webex meetings. Google Messages is testing MLS for RCS encryption. The protocol works at scale in production.

How NIP-EE maps MLS to Nostr

MLS assumes two infrastructure services exist: an Authentication Service that verifies identities, and a Delivery Service that routes messages. Signal has centralized servers for both. #nostr has neither.

NIP-EE, authored by JeffG (erskingardner) and merged into the NIPs repository in August 2025, maps these services onto Nostr’s existing infrastructure.

Authentication is handled by Nostr’s keypair identity system. Users publish MLS KeyPackages as kind 443 events, signed with their Nostr identity key. A KeyPackage contains the #cryptography credentials another user needs to add you to a group asynchronously. You publish them to relays listed in a kind 10051 event (your KeyPackage relay list), and anyone who wants to invite you to a group can fetch one.

Delivery is handled by Nostr relays. Group messages are kind 445 events, encrypted with keys derived from the MLS exporter_secret using NIP-44 encryption. They’re published using ephemeral keypairs, not your identity key, so your pubkey doesn’t appear on group messages.

Welcome messages (kind 444) use NIP-59 gift wrapping, the same metadata-hiding layer from NIP-17. When someone adds you to a group, the Welcome is sealed and wrapped so relays can’t see who’s being invited to what.

One detail that matters: NIP-EE uses a custom MLS ciphersuite built on secp256k1, the same elliptic curve Bitcoin and Nostr use. JeffG built openmls_nostr_crypto to replace the default RustCrypto crate with the Bitcoin-standard secp256k1 implementation. MLS signing keys are kept separate from Nostr identity keys, which means compromising someone’s nsec doesn’t expose their MLS group messages.

The merge happened after two independent implementations, White Noise and 0xchat, validated the spec. That’s a higher bar than most NIPs clear.

The Marmot Protocol

NIP-EE defined the core mapping. The Marmot Protocol extends it into a standalone specification with its own set of proposals (MIPs). MIP-00 through MIP-03 cover credentials, group construction, Welcome events, and group messages. MIP-04 adds encrypted media. MIP-05 handles push notifications.

The Marmot Development Kit (MDK) is a Rust library built on OpenMLS that handles group management, key rotation, and Nostr-specific abstractions. There’s also marmot-ts, an early-stage TypeScript implementation wrapping ts-mls.

JeffG has been the driving force behind all of this. The same person who authored NIP-EE built the reference implementation, the custom ciphersuite, and contributed Rust crates that Yuki Kishimoto is integrating into rust-nostr.

The project is experimental. The Marmot repo says “potential breaking changes” and “not recommended for production use.”

What changes, concretely

Property NIP-17 NIP-EE (MLS)
Forward secrecy No. Key compromise exposes all messages. Yes. Old epoch keys are deleted.
Post-compromise security No. Compromise is permanent. Yes. Commits refresh key material.
Metadata privacy Strong. Gift wrapping hides participants and timestamps. Moderate. Group ID visible in tags. Participant identities hidden.
Deniability Yes. Inner messages are unsigned. Partial. Application messages unsigned, but Commits are authenticated.
Group scaling O(N) per message. Practical limit ~100 members. O(log N) per key update. Designed for thousands.
Multi-device Works. Same nsec decrypts on any device. Complex. Each device is a separate MLS leaf node.
Identity key independence No. Encryption tied to Nostr identity keys. Yes. MLS keys separate from nsec.
Maturity Merged, widely implemented. Merged August 2025, two implementations. Superseded by Marmot.

The tradeoff is visible in the table. NIP-17 gives you better metadata #privacy. NIP-EE gives you stronger #cryptography guarantees and group scaling. They’re solving different problems.

Who’s building on this

White Noise is the reference implementation. Flutter frontend, Rust backend, built by JeffG’s team. It’s on Apple TestFlight and Android via ZapStore. Alpha quality, expect rough edges. Recent work added encrypted media sharing, where files are uploaded to Blossom servers with one-time keypairs.

0xchat had existing NIP-17 support for private groups and migrated to MLS. Their iOS App Store changelog shows progressive integration, from private chat to full MLS group creation.

Loxation is building an enterprise-grade MLS-over-Nostr stack with admin controls, automated KeyPackage provisioning, and SDKs for iOS and React Native. Different audience than the typical Nostr user, but it validates that the protocol design can support organizational use cases.

Outside Nostr, MLS adoption is accelerating. Wire went GA with full MLS migration. Discord’s DAVE protocol uses MLS for voice and video E2EE. The GSMA specified MLS for RCS encryption in March 2025, and Google Messages started testing it. These deployments matter because they prove the protocol works under real-world load, even if the #nostr mapping adds its own complications.

What’s still broken

I want to be specific about the open problems because some of them are fundamental, not just “needs more development time.”

Relay deletion and forward secrecy. MLS forward secrecy depends on KeyPackages being consumed and deleted after use. Nostr has no mechanism to force a relay to delete anything. A relay that retains old KeyPackages could undermine the forward secrecy chain. NIP-EE recommends selecting relays that support deletion and using freshness checks, but this is best-effort mitigation. It’s not a guarantee.

Commit ordering without a server. MLS was designed assuming a central Delivery Service that enforces total ordering of Commits. Nostr relays don’t coordinate ordering. NIP-EE handles this with a conflict resolution rule: when two Commits target the same epoch, the one with the lowest created_at wins. If timestamps match, lowest event ID wins. It’s pragmatic, but it’s weaker than server-enforced ordering. A malicious client could manipulate timestamps to preempt group updates.

Multi-device support. MLS treats each device as a separate group member with its own leaf in the ratchet tree. You can’t import your nsec on a second phone and decrypt your MLS groups. Each device needs to be added to every group independently. Nostr doesn’t have a standard for linking device keys to a single identity, there’s an open issue (nips#1810) but no solution yet.

Welcome message size. For groups above roughly 150 members, the MLS Welcome message contains enough ratchet tree state to exceed relay event size limits. The IETF is working on “light” client Welcomes, but it’s not standardized yet.

No security audit of Nostr-specific code. The MLS protocol itself (RFC 9420) has extensive formal analysis and vetting. The Nostr-specific adaptations, the custom ciphersuite, the exporter_secret key derivation, the relay-based delivery model, have not been independently audited. The underlying #cryptography is solid, but the glue code matters.

Metadata privacy is weaker than NIP-17. Group IDs appear in the h tag of kind 445 events. Relay operators can’t read message content or identify participants, but they can see that a group exists and observe its traffic patterns. NIP-17’s gift wrapping does a better job of hiding this kind of metadata.

The honest assessment

NIP-EE and the Marmot Protocol are doing something genuinely hard: bringing MLS’s cryptographic properties to a protocol that has no central server, no guaranteed message ordering, and no reliable deletion. Two independent implementations show the mapping can work.

But “can work” and “ready for use” are different things. If you need encrypted group messaging on #nostr today with forward secrecy, you’re an early adopter running alpha software with known limitations around relay trust and multi-device support.

NIP-17 remains the right choice for most Nostr DM use cases. It’s implemented across major clients, it hides metadata well, and for casual conversations the lack of forward secrecy is an acceptable tradeoff. If your threat model includes key compromise, NIP-17 is honest about its limits, and so is NIP-EE about its own.

The two specs complement each other. NIP-17 for #privacy-focused DMs where metadata protection matters most. NIP-EE for groups where forward secrecy, post-compromise security, and scale are the priority. The question isn’t which one wins. It’s whether Nostr relays can enforce the deletion and ordering guarantees that MLS assumes, and right now, they can’t.


No comments yet.