Skip to content

DavidGershony/openChat

Repository files navigation

OpenChat

A Marmot Protocol client for desktop and Android. End-to-end encrypted group messaging over Nostr, powered by MLS (RFC 9420).

.NET 9 Avalonia UI Marmot License

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.

What is Marmot?

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.

What does it do?

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)

Quick start

git clone https://github.com/DavidGershony/openChat.git
cd openChat
dotnet run --project src/OpenChat.Desktop

Log in with an nsec, generate a fresh key, or scan a QR code with Amber.

Features

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

Nostr & Marmot protocol support

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

NIPs implemented

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)

MLS Group Lifecycle (Marmot style)

  1. Publish KeyPackage (kind 443) — announce your MLS public key material to relays
  2. Create group — initialize an MLS group with the 0xF2EE NostrGroupData extension
  3. Invite — fetch the invitee's KeyPackage, add them to the MLS group, gift-wrap the Welcome (kind 444)
  4. Accept — unwrap the gift wrap, process the MLS Welcome, join the group
  5. Chat — encrypt messages with MLS + MIP-03 ChaCha20-Poly1305, publish as kind 445
  6. Forward secrecy — MLS ratchets keys with every commit

Security

  • 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

Project layout

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

Running 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.

Building the Rust MLS backend (optional)

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/  # Windows

Run with --mdk rust to use the Rust backend, or --mdk managed (default) for C#.

Releases

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.

Tech stack

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

Related projects

License

MIT

About

Nostr-based encrypted messenger with MLS group encryption

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors