White Noise v2026.3.5
Published on 5 Mar 2026

We shipped v0.3.0, found bugs that lost messages, and went quiet to fix them. v2026.3.5 is the result: messages that arrive instantly with delivery confirmation, group chats that no longer drop messages, improved image sharing, and Amber support. New CLI and terminal clients join the mobile app. A different product from where we started.
We shipped v0.3.0 one week ago and said nothing about it. Within hours of release we found bugs that caused messages to vanish from group chats to some users. Announcing a messenger that loses messages felt wrong, so we went quiet and fixed it instead. v2026.3.5 is the result: everything new from v0.3.0, with the reliability problems solved.
For anyone coming from an older version, this is a large update. New encryption under the hood, real-time message delivery, image sharing, group chats, Amber support, and a long list of improvements that make the app feel like a different product. Here is what changed and why:
Upgrading
If you are coming from v0.3.0, the upgrade is simple. Your existing groups stay intact, but the encryption method for new group messages has changed. Group members who have not yet upgraded to v2026.3.5 will not be able to read new messages or receive images until they update. Once everyone in a group is on v2026.3.5, everything works as expected.
If you are coming from an older version (v0.2.x or earlier), this upgrade requires a one-time reset of all local data. Messages, images, group memberships, and settings will be erased when you open the new version. The reason: the Marmot protocol changed how it manages encryption state for groups, and we moved the local database from unencrypted to fully encrypted storage. Least Authority's security audit confirmed encrypted storage as a priority, since a messenger built on privacy should not store conversations in plaintext on disk. Because the database library has no way to migrate between formats, a clean break was the only option that would not risk silent corruption.
Before upgrading from v0.2.x, back up your nsec (your private key) if you have not already. After updating, log in with your existing keys or connect through Amber. Your profile information lives on Nostr relays, so it will sync back automatically when you sign in. Follow list persists on relays too. Groups from v0.2.x will need to be recreated, since the old encryption state cannot carry over.
The database and protocol structures now support proper migrations, so future updates will preserve your data regardless of which version you are coming from.
Amber and External Signers
For users who manage their Nostr keys through a dedicated signing app like Amber, White Noise previously required you to paste your private key directly into the app. That meant trusting the app with your most sensitive credential. With this release, White Noise connects to Amber and other external signers directly, so your private key never leaves the signer. The app asks the signer to authorize each action, and the key stays where it belongs.
QR code scanning accepts both private keys (nsec) and public keys (npub) without requiring you to know which format you are scanning. If you have been waiting for external signer support before trying White Noise, this is the release.
Messages That Arrived Late Now Arrive
The most consequential fix in this release is invisible. In v0.3.0, messages sent during the brief window between receiving a Welcome and subscribing to the group relay would vanish. Late-arriving messages from previous MLS epochs were permanently marked as failed because max_past_epochs was never wired into the OpenMLS configuration (it defaulted to zero).
Both problems are fixed. The Rust backend now gates catch-up on subscription readiness instead of a fixed sleep timer, and max_past_epochs defaults to 5. Messages that arrive slightly out of order or from a previous epoch now decrypt correctly. OpenMLS was bumped to 0.8.1, which includes a security fix for tag comparison (GHSA-8x3w-qj7j-gqhf).
Relay Handling That Does Not Strand Users
v0.3.0 had a blind spot: if a user's inbox relay list (kind 10050) was missing, giftwrap subscriptions would silently fail. The user could log in but never receive messages.
This release falls back to NIP-65 relays when inbox relay lists are absent ([whitenoise-rs#518](https://github.com/marmot-protocol/whitenoise-rs/pull/518)). Login now blocks when any of the required relay lists (kinds 10002, 10050, 10051) are missing, preventing users from entering a broken state. Relay filter validation and semantic event selection were hardened. The NostrManager::with_signer path is now cancellation-safe. Subscriptions recover after external signer re-registration instead of going silent.
Message Delivery Status
Previous versions published messages with no feedback. The app fired events at relays and moved on. If relays rejected or never acknowledged the message, the sender had no way to know.
Publishing is now tracked end-to-end. Messages appear immediately with a Sending status, transition to Sent(N relays) after relay acknowledgment, or Failed(reason) after three automatic retries with exponential backoff (2s, 4s). The Rust backend streams status updates through flutter_rust_bridge, and the Flutter UI renders them as sending, sent or failed on each outgoing bubble ([whitenoise-rs#519](https://github.com/marmot-protocol/whitenoise-rs/pull/519), [whitenoise#425](https://github.com/marmot-protocol/whitenoise/pull/425)). Tap a failed message to retry manually. Retried messages reposition to the end of the chat list.
Delivery status persists across app restarts and survives relay echo reprocessing without regressing to stale state ([whitenoise-rs#559](https://github.com/marmot-protocol/whitenoise-rs/pull/559), [whitenoise-rs#556](https://github.com/marmot-protocol/whitenoise-rs/pull/556)).
MIP-04: Encrypted Media
MIP-04 defines how Marmot clients encrypt files for upload and decrypt them after download. The encryption key derives from the MLS group secret via HKDF-Expand, so only group members can access shared media. File metadata (MIME type, dimensions, blurhash) travels as an IMETA tag inside the encrypted MLS message.
The MDK exposes MIP-04 through UniFFI bindings for Kotlin, Swift, Python, and Ruby ([mdk#215](https://github.com/marmot-protocol/mdk/pull/215)). The HKDF key derivation was corrected to use expand-only semantics with the exporter secret as PRK, matching the specification exactly ([mdk#217](https://github.com/marmot-protocol/mdk/pull/217)). The TypeScript SDK implements MIP-04 natively with a GroupMediaStore, IMETA tag parsing, and event-driven media caching ([marmot-ts#55](https://github.com/marmot-protocol/marmot-ts/pull/55)).
Replies That Work Like Replies
Swipe-to-reply, auto-focus on the input field, and author-colored quote previews make replies feel like a natural part of conversation instead of an afterthought ([whitenoise#389](https://github.com/marmot-protocol/whitenoise/pull/389)). Tapping a reply scrolls to the original message. The reply context preserves media attachments in the quote.
Media Previews in Chat List
Conversations with image-only messages no longer show empty previews. The chat list displays an attachment indicator so you can tell what happened without opening the conversation ([whitenoise#414](https://github.com/marmot-protocol/whitenoise/pull/414)).
Group Security Hardening
Key packages are now validated before adding members to a group. The backend checks kind, base64 payload, ciphersuite, required MLS extensions, and verifies the event pubkey matches the expected member ([whitenoise-rs#548](https://github.com/marmot-protocol/whitenoise-rs/pull/548)). Admin-only mutations (add members, remove members, update group data) are enforced with fail-fast checks ([whitenoise-rs#547](https://github.com/marmot-protocol/whitenoise-rs/pull/547)). Post-join self-update is temporarily disabled to prevent stale-state divergence risk ([whitenoise-rs#562](https://github.com/marmot-protocol/whitenoise-rs/pull/562)).
UI Polish
Twenty-plus pull requests worth of visual fixes. The home screen layout was flattened with improved spacing for the hero image, slogan, and auth controls ([whitenoise#441](https://github.com/marmot-protocol/whitenoise/pull/441)). The signup bio field no longer gets cut off by the keyboard. The onboarding carousel text renders completely. The npub display uses block-based ellipsis instead of cutting mid-character. Relay input fields have a clear button. Error notices in the chat screen sit in the right position. Blurhash placeholders now match the dimensions of the loaded image instead of rendering at the wrong size. Disabled buttons are visible in both light and dark mode. The base design size matches the Figma source of truth.
Debug View
A new debug screen surfaces logs from the Rust backend directly in the app ([whitenoise#417](https://github.com/marmot-protocol/whitenoise/pull/417)). For developers and testers, this eliminates the need to connect to ADB or read logcat output to diagnose issues.
CI Pipeline
Tests now shard four ways with Rust test coverage included ([whitenoise#419](https://github.com/marmot-protocol/whitenoise/pull/419)). A 10-minute timeout prevents stuck jobs from blocking the queue. APK builds run on every pull request. The release builder and Zapstore asset pipeline shipped alongside v0.3.0 and continue to work for this release.
New Clients and Tools
whitenoise-rs CLI. A full CLI client and daemon that exposes the Rust backend directly from the command line ([whitenoise-rs#537](https://github.com/marmot-protocol/whitenoise-rs/pull/537)). Commands cover identity management, accounts, groups, chats, messages, follows, users, profile, relays, settings, notifications, and streaming IPC. Includes end-to-end test suite.
wn-tui. A terminal client for White Noise, written in Rust. Unicode-safe input, auto-growing composer, reactions, follows management, a status bar with display name and invite count, and a profile screen with bech32 npub conversion. For people who live in the terminal and prefer it that way. ([wn-tui](https://github.com/marmot-protocol/wn-tui))
We tested interoperability (including encrypted media) between the new Marmot typescript library with MDK apps using the pre-alpha Marmots web chat (and it worked!) https://github.com/marmot-protocol/marmot-ts
Dr. Marmot. A diagnostic tool that audits your Nostr identity for Marmot compatibility. Checks relay unification across kinds 10002, 10050, and 10051. Detects orphaned key packages. Prescribes fixes. Works on mobile. Has a personality layer called "Jeff mode" for when you want your diagnostics delivered as corporate wellness advice. ([dr.marmot](https://github.com/marmot-protocol/dr.marmot))
Protocol
The Marmot specification updated three MIPs:
- MIP-01: Extension format v2 with QUIC varint encoding. Admin pubkeys changed from hex-encoded comma-separated strings to concatenated raw 32-byte x-only public keys.
- MIP-02: Clarified post-join self-update sequencing with a warning about stale-state divergence risk.
- MIP-03: NIP-44 replaced with ChaCha20-Poly1305 (described above). MIP-04 exporter label updated from ("nostr", "nostr") to ("marmot", "encrypted-media").
SDKs
The MDK shipped three releases in this cycle. v0.6.0 added
clear_pending_commitfor recovering from publish failures, ratchet tree inspection with public keys, and propermax_past_epochswiring. v0.7.0 exposed MIP-04 encrypted media through UniFFI bindings withencrypt_media_for_upload,decrypt_media_from_download,create_media_imeta_tag, andparse_media_imeta_tag([mdk#215](https://github.com/marmot-protocol/mdk/pull/215)). v0.7.1 fixed MIP-04 file key derivation to use HKDF-Expand with the exporter secret as PRK ([mdk#217](https://github.com/marmot-protocol/mdk/pull/217)).A CI workflow now checks UniFFI binding coverage against the mdk-core public API surface on every pull request, using nightly rustdoc JSON to compare bound vs. unbound methods. Current coverage: 30/37 bindable methods (81.1%) ([mdk#212](https://github.com/marmot-protocol/mdk/pull/212)).
All four language bindings (Python, Ruby, Kotlin, Swift) received automated updates tracking 14 core MDK releases in this period.The TypeScript SDK added a
KeyPackageManagerclass withwatchKeyPackagesandmarkUsed()tracking, typed domain errors replacing generic throws,decryptGroupMessage(renamed fromreadGroupMessage), debug logging via thedebugpackage, and an anti-fork fix that prevents automatic self-update injoinGroupFromWelcome. MIP-04 support includes aGroupMediaStorewith event-driven caching, IMETA tag helpers, and per-file encrypt/decrypt methods ([marmot-ts#55](https://github.com/marmot-protocol/marmot-ts/pull/55)). The npm package was renamed from@internet-privacy/marmotsto@internet-privacy/marmot-ts([marmot-ts#59](https://github.com/marmot-protocol/marmot-ts/pull/59)).
Added
- Message delivery status: sending, sent (with relay count), failed (with reason) ([whitenoise-rs#519](https://github.com/marmot-protocol/whitenoise-rs/pull/519), [whitenoise#425](https://github.com/marmot-protocol/whitenoise/pull/425))
- Tap-to-retry for failed messages with automatic repositioning ([whitenoise#442](https://github.com/marmot-protocol/whitenoise/pull/442))
- MIP-04 encrypted media in UniFFI bindings for Kotlin/Swift/Python/Ruby ([mdk#215](https://github.com/marmot-protocol/mdk/pull/215))
- MIP-04 encrypted media in TypeScript SDK with GroupMediaStore ([marmot-ts#55](https://github.com/marmot-protocol/marmot-ts/pull/55))
- Chat system messages showing inviter on invite screens ([whitenoise#408](https://github.com/marmot-protocol/whitenoise/pull/408))
- Per-message sender name and avatar in group chats ([whitenoise#442](https://github.com/marmot-protocol/whitenoise/pull/442))
- CLI client and daemon for whitenoise-rs ([whitenoise-rs#537](https://github.com/marmot-protocol/whitenoise-rs/pull/537))
- Key package compatibility validation before member adds ([whitenoise-rs#548](https://github.com/marmot-protocol/whitenoise-rs/pull/548))
- Admin-only mutation enforcement for group operations ([whitenoise-rs#547](https://github.com/marmot-protocol/whitenoise-rs/pull/547))
- UniFFI bindings coverage CI checker ([mdk#212](https://github.com/marmot-protocol/mdk/pull/212))
- Swipe-to-reply with auto-focus and author-colored quotes ([#389](https://github.com/marmot-protocol/whitenoise/pull/389))
- Media-only chat summary in chat list ([#414](https://github.com/marmot-protocol/whitenoise/pull/414))
- Clear button on relay input fields ([#412](https://github.com/marmot-protocol/whitenoise/pull/412))
- Debug view with Rust backend logs ([#417](https://github.com/marmot-protocol/whitenoise/pull/417))
- APK builds on every pull request ([#401](https://github.com/marmot-protocol/whitenoise/pull/401))
- Follows support in wn-tui ([wn-tui#1](https://github.com/marmot-protocol/wn-tui/pull/1))
- README pixel art ([#436](https://github.com/marmot-protocol/whitenoise/pull/436))
Changed
- MIP-03 encryption: NIP-44 replaced with ChaCha20-Poly1305 for kind:445
- MIP-01 extension format v2 with QUIC varint encoding
- MIP-04 exporter label updated; HKDF key derivation corrected to expand-only semantics ([mdk#217](https://github.com/marmot-protocol/mdk/pull/217))
- MDK bumped to v0.7.1 with MIP-04 encrypted media support
- TypeScript SDK npm package renamed from @internet-privacy/marmots to @internet-privacy/marmot-ts ([marmot-ts#59](https://github.com/marmot-protocol/marmot-ts/pull/59))
- Message publishing replaced fire-and-forget with tracked delivery status and automatic retries ([whitenoise-rs#519](https://github.com/marmot-protocol/whitenoise-rs/pull/519))
- Post-join self-update temporarily disabled to prevent stale-state divergence ([whitenoise-rs#562](https://github.com/marmot-protocol/whitenoise-rs/pull/562))
- Home screen layout flattened with improved visual hierarchy ([whitenoise#441](https://github.com/marmot-protocol/whitenoise/pull/441))
- OpenMLS bumped to 0.8.1
- Base design size aligned to Figma ([#433](https://github.com/marmot-protocol/whitenoise/pull/433))
- CI sharded 4-way with Rust tests and 10-min timeout ([#419](https://github.com/marmot-protocol/whitenoise/pull/419), [#431](https://github.com/marmot-protocol/whitenoise/pull/431))
- whitenoise-rs: groups and users split into focused submodules ([#541](https://github.com/marmot-protocol/whitenoise-rs/pull/541))
Fixed
- iOS crash from duplicate tracing initialization ([#430](https://github.com/marmot-protocol/whitenoise/pull/430))
- Messages lost during Welcome-to-subscription gap ([whitenoise-rs#526](https://github.com/marmot-protocol/whitenoise-rs/pull/526))
- Late-epoch messages permanently marked failed ([whitenoise-rs#525](https://github.com/marmot-protocol/whitenoise-rs/pull/525), [mdk#207](https://github.com/marmot-protocol/mdk/pull/207))
- Giftwrap subscriptions failing when inbox relay list missing ([whitenoise-rs#518](https://github.com/marmot-protocol/whitenoise-rs/pull/518))
- Welcome accepted without key-package e-tag ([whitenoise-rs#539](https://github.com/marmot-protocol/whitenoise-rs/pull/539))
- Subscriptions lost after external signer re-registration ([whitenoise-rs#512](https://github.com/marmot-protocol/whitenoise-rs/pull/512))
- Delivery status preserved on echoed outgoing messages ([whitenoise-rs#559](https://github.com/marmot-protocol/whitenoise-rs/pull/559))
- Retry status updates streamed to subscribers immediately ([whitenoise-rs#556](https://github.com/marmot-protocol/whitenoise-rs/pull/556))
- Relay error propagation from sync_relay_urls ([whitenoise-rs#545](https://github.com/marmot-protocol/whitenoise-rs/pull/545))
- Media blurhash placeholder sizing to match loaded image dimensions ([whitenoise#442](https://github.com/marmot-protocol/whitenoise/pull/442))
- Message bubble padding and group chat sender info display ([whitenoise#442](https://github.com/marmot-protocol/whitenoise/pull/442))
- MIP-04 HKDF key derivation corrected to expand-only with exporter secret as PRK ([mdk#217](https://github.com/marmot-protocol/mdk/pull/217))
- Signup bio field cut by keyboard ([#435](https://github.com/marmot-protocol/whitenoise/pull/435))
- Onboarding carousel text cut off ([#404](https://github.com/marmot-protocol/whitenoise/pull/404))
- Media modal blurhash rendering ([#397](https://github.com/marmot-protocol/whitenoise/pull/397))
- Npub ellipsis display ([#388](https://github.com/marmot-protocol/whitenoise/pull/388))
- Chat screen error notice positioning ([#409](https://github.com/marmot-protocol/whitenoise/pull/409))
- Translation fixes including relay validation messages ([#396](https://github.com/marmot-protocol/whitenoise/pull/396))
- Duplicate DM chats with same peer ([#371](https://github.com/marmot-protocol/whitenoise/pull/371))
- TypeScript SDK event listener setup order in group subscribe ([marmot-ts#55](https://github.com/marmot-protocol/marmot-ts/pull/55))
Contributors
JeffG (erskingardner), Pepi (josefinalliende), Danny M (dannym-arx), emir yorulmaz (untreu2), Javier G. Montoya S. (jgmontoya), hzrd149, gzuuus, Mubarak Auwal (mubarakcoded), JSKitty, Andrew (dechristopher).
Full Changelog
For the complete list of changes, see the changelogs in each repository:
- [whitenoise](https://github.com/marmot-protocol/whitenoise/blob/main/CHANGELOG.md)
- [whitenoise-rs](https://github.com/marmot-protocol/whitenoise-rs)
- [mdk](https://github.com/marmot-protocol/mdk)
- [marmot](https://github.com/marmot-protocol/marmot)
- [marmot-ts](https://github.com/marmot-protocol/marmot-ts)
---
Download: [github.com/marmot-protocol/whitenoise/releases](https://github.com/marmot-protocol/whitenoise/releases)
Diagnose your setup: [dr.marmot](https://github.com/marmot-protocol/dr.marmot)
Report issues: [github.com/marmot-protocol/whitenoise/issues](https://github.com/marmot-protocol/whitenoise/issues)
Community call: First Tuesday of each month, 1700 UTC