A Marmot Protocol client for desktop and Android. End-to-end encrypted group messaging over Nostr, powered by MLS (RFC 9420).
Fair warning: This entire app was vibe coded with AI. Every feature, every bug fix, every test. The humans mostly pointed and said "make it work" and "that's broken, fix it." It works surprisingly well, but you've been warned.
Marmot is a protocol for encrypted group messaging on Nostr. It uses MLS (Messaging Layer Security, RFC 9420) to give you forward secrecy and post-compromise security in group chats — the same kind of encryption Signal uses, but decentralized over Nostr relays.
OpenChat is a Marmot client. It implements the Marmot Improvement Proposals (MIPs):
| MIP | What it defines | OpenChat status |
|---|---|---|
| MIP-00 | KeyPackage publishing (kind 443) | Implemented |
| MIP-01 | Group metadata & NostrGroupData extension (0xF2EE) | Implemented |
| MIP-02 | Welcome events (kind 444) with NIP-59 gift wrap | Implemented |
| MIP-03 | Group messages (kind 445) with ChaCha20-Poly1305 | Implemented |
| MIP-04 | Encrypted media attachments via Blossom | Implemented |
Any Marmot-compatible client can join the same groups and exchange messages with OpenChat.
Encrypted group chat over Nostr. Your messages are encrypted before they leave your machine. Relays just pass around blobs they can't read. No accounts, no phone numbers, no email signups. Just keys.
- Marmot group chats — MLS encryption with forward secrecy
- Direct messages — NIP-44 encryption
- Voice messages — record, send, and play back audio with Opus encoding
- Image & file sharing — MIP-04 encrypted media via Blossom upload
- Emoji reactions — react to group messages with any emoji
- Login with Amber (NIP-46) or a local nsec — your choice
- Desktop (Windows/Linux/macOS) and Android
- Two MLS backends — Rust (Marmot MDK) or pure C# (marmot-cs)
- Interoperable — works with any Marmot protocol implementation
- Themes — runtime theme switching (Nostr, Golden Axe, and more on Android)
git clone https://github.com/DavidGershony/openChat.git
cd openChat
dotnet run --project src/OpenChat.DesktopLog in with an nsec, generate a fresh key, or scan a QR code with Amber.
| Feature | Status |
|---|---|
| Marmot MLS encrypted group chat (MIP-00 to MIP-03) | Working |
| Cross-client interop (any Marmot implementation) | Working |
| MIP-04 encrypted image & file sharing (Blossom upload) | Working |
| Voice messages (record, send, play) | Working |
| Emoji reactions on group messages | Working |
| NIP-59 gift-wrapped Welcome invites | Working |
| NIP-42 relay authentication | Working |
| NIP-46 remote signer (Amber) with auto-reconnect | Working |
| NIP-65 relay discovery | Working |
| KeyPackage auto-lookup on invite | Working |
| KeyPackage audit (detect lost/expired packages) | Working |
| Load older messages from relays | Working |
| Profile metadata publish (kind 0) | Working |
| Profile dialog with npub/nsec export | Working |
| Secure key storage (DPAPI / Android Keystore) | Working |
| Dual MLS backend (Rust MDK + C# marmot-cs) | Working |
| Runtime theme switching | Working |
| Direct messages (NIP-44) | Basic |
OpenChat maps MLS operations to Nostr event kinds. Your client publishes a KeyPackage so others can invite you. Invites arrive as NIP-59 gift-wrapped Welcome events. Once you join a group, messages are MLS-encrypted and published with the group's NostrGroupId in the h tag.
| Nostr Event Kind | Usage |
|---|---|
| 443 | MLS KeyPackage — "here's how to invite me" (MIP-00) |
| 444 | MLS Welcome — group invite, gift wrapped via NIP-59 (MIP-02) |
| 445 | Group message — MLS + ChaCha20-Poly1305 encrypted (MIP-03) |
| 1059 | NIP-59 gift wrap envelope for Welcome events |
| 22242 | NIP-42 relay authentication response |
| 7 | Emoji reactions |
| 0 | User profile metadata |
| 10002 | NIP-65 relay list |
| NIP | What |
|---|---|
| NIP-01 | Basic Nostr protocol (events, subscriptions, relays) |
| NIP-42 | Relay authentication (auto-responds to AUTH challenges) |
| NIP-44 | Encrypted direct messages |
| NIP-46 | External signer (Amber) via nostrconnect |
| NIP-59 | Gift wrap (sealed sender for Welcome events) |
| NIP-65 | Relay list discovery (kind 10002) |
- Publish KeyPackage (kind 443) — announce your MLS public key material to relays
- Create group — initialize an MLS group with the 0xF2EE NostrGroupData extension
- Invite — fetch the invitee's KeyPackage, add them to the MLS group, gift-wrap the Welcome (kind 444)
- Accept — unwrap the gift wrap, process the MLS Welcome, join the group
- Chat — encrypt messages with MLS + MIP-03 ChaCha20-Poly1305, publish as kind 445
- Forward secrecy — MLS ratchets keys with every commit
- Event signature verification — BIP-340 Schnorr signatures verified on all received events
- Relay URL validation — blocks connections to private/reserved IP ranges (SSRF protection)
- Encrypted key storage — DPAPI on Windows, Android Keystore on mobile
- NIP-46 session encryption — ephemeral keypairs for external signer communication
- SQLite WAL mode — write-ahead logging with serialized writes for concurrency safety
- Thread-safe relay connections — ConcurrentDictionary for all shared state
- MIP-03 ephemeral signing — kind 445 events signed with ephemeral keys for sender privacy
src/
OpenChat.Core/ Nostr protocol, MLS integration, Marmot MIP implementations
OpenChat.Presentation/ ViewModels (ReactiveUI + Fody)
OpenChat.UI/ Avalonia views, themes, and platform services
OpenChat.Desktop/ Desktop entry point
OpenChat.Android/ Android entry point
OpenChat.Native/ Rust native library (Marmot MDK FFI)
tests/
OpenChat.Core.Tests/ Unit, integration, real-relay, and MIP compliance tests
OpenChat.UI.Tests/ ViewModel tests
# Unit tests (fast, no relay needed)
dotnet test --filter "Category!=Relay&Category!=Integration"
# Real relay integration tests (needs docker)
docker compose -f docker-compose.test.yml up -d
dotnet test --filter "Category=Integration"The integration tests run a full Marmot flow through a real Nostr relay: create group, publish KeyPackage, send Welcome, exchange encrypted messages, verify MIP-03 wire format.
The pure C# MLS backend (marmot-cs) works out of the box. The Rust backend (Marmot MDK) is optional:
cd src/OpenChat.Native
cargo build --release
cp target/release/openchat_native.dll ../OpenChat.Desktop/ # WindowsRun with --mdk rust to use the Rust backend, or --mdk managed (default) for C#.
Pre-built binaries are published via GitHub Actions on every tagged release:
- Windows (x64)
- Linux (x64)
- macOS (ARM64 / Apple Silicon)
- Android (APK, signed)
Download from the Releases page.
| What | How |
|---|---|
| Protocol | Marmot (MIP-00 through MIP-04) |
| MLS | Marmot MDK (Rust) + marmot-cs (C#) |
| Transport | Nostr (NIP-01, NIP-42, NIP-44, NIP-46, NIP-59, NIP-65) |
| UI | Avalonia 11 |
| MVVM | ReactiveUI + Fody |
| Audio | Opus codec (recording + playback) |
| Media | Blossom for encrypted file uploads |
| Storage | SQLite (WAL mode) + DPAPI/Android Keystore for key encryption |
| Logging | Serilog |
| Runtime | .NET 9 |
| Vibes | Claude |
- Marmot Protocol — the protocol spec and reference implementation
- Marmot MDK — Rust MLS development kit
- marmot-cs — pure C# Marmot implementation
- marmot-ts — TypeScript/web Marmot client
- dotnet-mls — C# MLS (RFC 9420) implementation
MIT