Marmot Protocol Monthly Recap: May 2026
- Protocol and SDK
- Amethyst adds replies inside Marmot groups
- White Noise: iOS push, markdown, deep links
- Scramble shipped multi-device
- Vector rebuilt its insides
- AgentNoise: a non-chat use of the protocol
- Infrastructure and other projects
- A week in Oslo, and the work that didn’t ship
- Cordn: a centralized fork on the other side of the trade-off
- What comes next
- By the Numbers
- Contributors
May was a multi-client month for Marmot. MDK shipped v0.8.0, wired disappearing messages all the way through the core library and into iOS and Android via a unified UniFFI surface, and tightened two separate security paths. Amethyst added thread replies inside Marmot groups. White Noise shipped iOS push notifications and a polish release covering markdown, deep links, and audio waveforms. Scramble shipped multi-device. Vector rebuilt its insides. AgentNoise opened up a new use of the protocol that has nothing to do with chat. And a separate team shipped Cordn, the first public alternative to Marmot’s coordination model. The visible commit graph in the Marmot org looked thinner than usual; some of the team’s energy went into work that has no public repo yet. We’ll get to that.
Protocol and SDK
The spec repo had a deliberately slow month. One change merged, one is open. MIP-01 was tightened to specify HKDF-SHA256 and a canonical input encoding for image key derivations (PR #68), closing an ambiguity that would have let two clients quietly derive different keys for the same image. The open proposal (#67) would rate-limit self-update commits at the protocol layer to stop a misbehaving client from spamming epoch advances.
MDK shipped v0.8.0 on May 4. The release pulled in the addressable key packages from April’s spec work, so a key-package announcement now replaces itself in place on relays. Rotation no longer scatters stale events behind every key. The first MIP-05 building blocks for per-recipient push notifications landed alongside (#235), mdk-core returned to crates.io (#273), and the test harness opened up to external client suites through a new test-utils Cargo feature (#269).
The bigger user-facing land came on May 22. PR #306 wired disappearing messages all the way through MDK, with validation, NIP-40 expiration handling, and a UniFFI surface that exposes both to native clients. iOS and Android now run the same Rust code to decide when a message expires. Any conformant Marmot client honors the same expiration semantics. One client expiring messages while another caches them forever is no longer a portability hazard. The third part of the trilogy, which adds deletion APIs and secure delete, is open as PR #315 and lands next.
Two security passes also went in. Welcome-failure reasons can no longer balloon into oversized stored strings and are sanitized before persistence (#307). Non-admin proposals can no longer ride into an admin commit alongside an admin-only proposal type, closing a category of privilege confusion (#317). The language bindings stay in lockstep through generated commits: Kotlin, Python, Ruby, and Swift each pulled tens of updates this month. The TypeScript SDK shipped a VitePress documentation site, and the reference web chat moved off ts-mls directly onto @internet-privacy/marmot-ts.
Audio attachments now travel with metadata. FileMetadata on the Rust side grew duration_ms and waveform fields (whitenoise-rs #833), and MDK added the same fields to MIP-04 media tags (#300). Voice messages can now render with a real waveform in place of the generic placeholder bar.
Amethyst adds replies inside Marmot groups
Amethyst shipped v1.11.0 in late May. PR #2995 adds reply support for Marmot/MLS group messages, so threads inside encrypted groups now render with the same parent-reference UI that public notes have always had. Amethyst built its pure-Kotlin Marmot stack last month, which is the work that made this kind of incremental polish possible: when the protocol implementation is yours, threading inside groups is a UI patch, not a multi-week bindings project.
White Noise: iOS push, markdown, deep links
Two White Noise releases shipped: v2026.5.7+24 on May 7 and v2026.5.22+25 on May 22. The May 22 release is the iOS push release; the May 7 release covered markdown rendering, native deep links, and audio waveforms.
iPhone users no longer need to keep White Noise foregrounded to get a message. iOS push lands through a Notification Service Extension (PR #673) that decrypts the MLS message inside the extension process and surfaces it as a system notification. Your phone shows the message. Apple’s push broker, sitting between the relay and the device, sees only ciphertext at no point in the flow. Android has the same property, where Transponder routes encrypted tokens through APNS, FCM, and UnifiedPush.
Signal and other e2e messengers have shipped push notifications for years, with message content encrypted to the device. The shape of the metadata improvement here is different. Signal still routes its push traffic through a Signal-operated server that knows who is messaging whom. Marmot has no central messaging server at all. Push routing is distributed across Nostr relays plus Transponder, the Marmot push server, none of which hold long-term user identity or maintain a social graph. The push provider still sees device tokens and timing the way it does for any other app; the central operator who knows “user A is talking to user B” does not exist in the Marmot model.
The May 7 release filled in rendering and routing. Chat now renders markdown (PR #665), backed by a new whitenoise-markdown crate (whitenoise-rs #836) that replaced an older token parser borrowed from nostr-sdk. Native deep links arrived in PR #661: tapping a whitenoise:// link from another app drops you into the right user profile, the right chat, or the right settings screen. A whitenoise-staging:// scheme ships alongside for builds running on the test channel.
Some long-overdue social plumbing also got tied off. An “Add members” button now sits on the group-info screen, where it belonged from the beginning (PR #679). Block and unblock got the full UX with confirmation flows and contact-list filtering (PR #676). Leave-group is now available to non-last admins, where the flow was previously stuck (PR #675). Share-via-long-press for media and messages (PR #687) and a dedicated iOS notification-settings screen (PR #688) round it out.
A quiet but important fix sits underneath. Key package rotation now reuses the d_tag slot for kind:30443 publishes (whitenoise-rs #835), so NIP-33 replaceable semantics behave the way the spec describes. Each rotation overwrites the previous event on relays.
Scramble shipped multi-device
Scramble, the renamed and expanded version of David Gershony’s former openChat, is a .NET/Avalonia desktop and Android client that implements MIPs 00 through 04 and stays fully interoperable with White Noise and any other conforming Marmot client. The project shipped 13 releases in May, with multi-device support as the headline.
The mechanics are good to know. Each device generates a unique KeyPackage slot keyed by d-tag on kind:30443. On startup, Scramble fetches its own KeyPackages from relays, detects the other devices the user owns, and adds them to existing MLS groups using staged commits. Auto-add only runs in groups where the user is admin; non-admin groups are skipped with a small in-app hint to ask the group admin. A forward-secrecy banner informs newly linked devices that older messages are unavailable. A reconciliation pass handles devices migrated from pre-multi-device versions by matching relay KeyPackage bytes against local key material.
The underlying marmot-cs library moved in lockstep. Staged-commit APIs for MIP-03 publish-then-merge landed, along with a ProcessIncomingCommitAsync path with MIP-03 tiebreaker resolution, MIP-03 nonce uniqueness tracking via a DuplicateNonceException, and a StageGroupDataUpdateAsync for MLS GroupContextExtensions proposals. dotnet-mls gained a HasPendingCommit property and a fix for an HKDF.Expand crash on Linux with OpenSSL 3.x by way of a managed RFC 5869 implementation.
Vector rebuilt its insides
Vector shipped v0.4.0 on May 26 as the largest release in the project’s history: 165 commits in May. A ground-up rewrite is the headline. All of Vector’s logic now lives in a decoupled crate called vector-core, shared across desktop, Android, and any future client. Thousands of lines shed from the application shell as the core pulled the protocol code under one roof. The rewrite is groundwork for a Vector CLI, bots, and SDKs that all drive the same protocol code as the GUI.
One-click Tor with bridge support landed for users who need traffic routing. Multi-account support landed with an in-app switcher. Remote-signer login arrived through NIP-46 with bunker pairing by QR or pasted URI. Delete-for-everyone works across NIP-17 DMs and Marmot group chats, with a spec divergence the release notes flag directly: Vector retains the ephemeral signing key so receiving clients can verify a deletion came from the original sender. The Vector team called out the trade-off in the release notes, which is the right move when a client steps outside the baseline. Per-recipient relay routing and per-account store isolation also landed. JSKitty pushed the rewrite; YuurinBee shipped the UI polish layer across eight merged PRs.
AgentNoise: a non-chat use of the protocol
AgentNoise, from nvk, is a small Rust desktop helper that does one strange and lovely thing: it lets you use a phone running White Noise as the control surface for a coding agent running on your laptop. You pair the phone once with a PIN. After that, sending /claude
AgentNoise drives the wn and wnd CLIs from whitenoise-rs as subprocesses, inheriting the Marmot transport directly. The Nostr layer, MLS, and the trusted bridge path all stay inside the Rust process; Node never enters the picture. v0.1.24 added shorter phone-readable replies, job references by short unique prefix, and an opt-in local session watcher.
AgentNoise joins a small but growing set of AI controllers running over Marmot. The pattern generalizes well: anything you can drive over a chat interface, you can now drive over a private, end-to-end-encrypted one. Worth watching what shows up next.
Infrastructure and other projects
The Marmot push notification server Transponder merged 19 PRs in May. Sliding rate-limit windows replaced the previous fixed-window implementation (#56). The push-auth cache now invalidates on upstream auth rejection (#55). Logging policy moved to startup environment variables with quieter defaults (#58). An APNS NSE probe payload mode landed for staging (#59) to support the iOS push work in White Noise.
The White Noise terminal client got a small but meaningful fix. Admins are now blocked from leaving a group with clear in-app guidance (wn-tui #7), closing the same admin-handoff edge case the protocol work covered last month.
The awesome-marmot catalog grew with Amethyst (Quartz integration), the amy interop CLI, Burrow, marmot-cli, marmot-server, dockerized-marmot-cli, openclaw-marmot, Scramble, marmot-cs, hermes-marmot, and NostrBotKit.
A week in Oslo, and the work that didn’t ship
The team met in Oslo for the AOS convergence and stayed through the Oslo Freedom Forum, which ran a Nostr-focused hackathon on the side. Most of the in-person time went into the next-phase architecture: how to rework the way Marmot scales when groups get large and out-of-group members fall out of consensus, the rough edge anyone running a Marmot group at real size has felt. Those conversations are the reason the public commit graph in the Marmot org looked thinner this month. A substantial rework of the spec, the library, and the clients is in flight and is not public yet. More to share when there is something concrete to point at.
Cordn: a centralized fork on the other side of the trade-off
A separate team shipped Cordn this month, the first public alternative to how Marmot orders messages inside a group. The short version: Cordn centralizes the group around a per-group coordinator. Marmot keeps the group decentralized. Same MLS cryptography underneath, same Nostr identity and transport layer, different bet on where the coordination cost lives.
Marmot’s bet is that group ordering can be done with no privileged actor at all. Every member writes commits and welcome events directly to relays. MLS’s own epoch and commit machinery handles ordering inside the group, and clients reconcile concurrent commits when relay delivery races a state transition. Admins pay for that arrangement with discipline: an admin has to wait for the relay to acknowledge a commit before sending the matching welcome, and clients have to do real work when two commits arrive out of order. What they get back is that no single party can take a group offline, censor a commit, or learn the timing of group activity beyond what relays already see.
Cordn’s bet is that the discipline is too much, and a per-group coordinator should handle ordering and welcome distribution. The coordinator is implemented as a ContextVM service. The trade is concrete: you accept a single point of liveness (and a single point of observation, even when the coordinator runs on your own infrastructure) in exchange for tighter ordering and a simpler client.
We think decentralized messaging is worth the engineering cost. Centralized messengers already exist, and most of them do the messaging part well. The reason Marmot exists is that the rest of what those messengers are good at is the opposite of what private group messaging should optimize for. The Cordn team disagrees. We’re glad that disagreement is happening in public, with shipping code on both sides. Repos sit at Cordn-msg/cordn and Cordn-msg/cordn-web, with a web client at cordn.net.
What comes next
The disappearing-messages trilogy lands its third part in MDK, adding deletion APIs and secure delete (#315). Android integration testing brings its harness to parity with iOS (whitenoise #690). And the work the Oslo trip seeded continues in private. We will surface it when it is ready to be looked at, not before.
By the Numbers
| Metric | Value |
|---|---|
| Tracked repositories | 60 |
| Active repositories | 20 |
| Releases across all tracked projects | 5 |
| Merged PRs | 198 |
| Commits | 643 |
| Marmot spec PRs merged | 1 |
| MDK releases | 1 (v0.8.0) |
| White Noise mobile releases | 2 |
| Vector releases | 1 (v0.4.0) |
| Amethyst releases | 1 (v1.11.0) |
Contributors
Thanks to everyone who contributed code, design, specs, issue reports, and planning this month: erskingardner, dannym-arx, mubarakcoded, pepina-dev, jgmontoya, YuurinBee, Datawav, nvk, hzrd149, stupidloud, JSKitty, DavidGershony, vitorpamplona, leesalminen, sjmcnamara, maddadder, sergey3bv, and Tuxor (NostrBotKit).
Start with the Marmot Protocol spec, MDK, or one of the clients: White Noise, Amethyst, Scramble, or Vector. The TypeScript SDK ships as @internet-privacy/marmot-ts. The Internet Privacy Foundation sits at ipf.dev.
Write a comment