SOMS: An Open-Source, Federated Chat Platform Built with Rust, NestJS, and Tauri
SOMS (Simple Open Messaging Service) is an open-source, self-hosted messaging platform designed for independent servers and cross-server communication. It provides real-time messaging, group chats, rich media streaming, and WebRTC audio/video calls across independent domains without a central coordinator or shared database.
When designing the platform and its underlying wire protocol (SOMFP - Simple Open Messaging Federation Protocol), our primary goal was practical resilience for personal servers, NAS devices, and home infrastructure.
This article details the architecture of SOMS: how the pull-based synchronization protocol functions, how native clients share a unified Rust core, and how the system operates reliably without external message broker dependencies.
1. Core Feature Overview
SOMS consists of three cohesive layers: the backend server, an administration console, and client applications sharing a common interface across web and native devices.
+-----------------------------------------------------------------------------+ | SOMS | +-----------------------------------------------------------------------------+ | Messaging & Media | - Rich text, replies, @mentions, link previews | | | - Image, streaming video, and voice messages | | | - SHA-256 verified media transfers | +------------------------+----------------------------------------------------+ | Federation (SOMFP) | - Ed25519-signed server-to-server HTTP APIs | | | - Monotonic cursor pull sync (zero data loss) | | | - First-class deletion feeds (tombstones) | +------------------------+----------------------------------------------------+ | Calling & Realtime | - One-to-one WebRTC voice and video calls | | | - Cross-server call signaling | | | - Server-Sent Events (SSE) for realtime client sync| +------------------------+----------------------------------------------------+ | Server Administration | - Role-based access control (RBAC) & user quotas | | | - In-app content reporting & administrative delete | | | - OTP, QR code, 2FA, and WebAuthn hardware keys | | | - Fastify + PostgreSQL + Caddy (Single Docker stack| +------------------------+----------------------------------------------------+ | Cross-Platform Clients| - Web / PWA (Angular Zoneless) | | | - Android (Native APK with Kotlin / Rust bridge) | | | - Desktop: Linux (deb/AppImage), macOS, Windows | +-----------------------------------------------------------------------------+ 2. Federation Architecture: Monotonic Pull Synchronization
Federation in SOMS connects autonomous servers. Rather than maintaining long-lived cross-server WebSocket meshes or unbounded outbound retry queues, SOMFP uses an append-only, cursor-driven pull model.
The Problem of Disconnected Peers
In home environments and self-hosted clusters, servers frequently encounter temporary downtime—ISP reconnects, dynamic IP changes, or routine hardware reboots. If servers attempt to push events directly to an offline peer, several issues arise:
- Senders must queue outbound traffic indefinitely.
- Missed notifications require complex graph reconciliation algorithms.
- Deletions or retractions sent during downtime can be lost permanently.
How SOMS Resolves State: Push to Notify, Pull to Sync
In SOMS, outbound push notifications are strictly advisory triggers. Authoritative synchronization always occurs through a monotonic feed traversed by the consumer.
+------------------+ +------------------+ | Server Node A | | Server Node B | +------------------+ +------------------+ | | | 1. Ephemeral Wakeup (Ed25519 Signed HTTP POST) | | "New feed head available at seq: 2400" | |-------------------------------------------------->| | | | 2. Catch-up Pull Request | | GET /federation/v1/feed?after=2380 | |<--------------------------------------------------| | | | 3. Monotonic Sequenced Batch Stream | | [Events 2381..2400 with cryptographic proofs] | |-------------------------------------------------->| | | | 4. Node B commits local cursor to 2400 | | Atomic transaction in PostgreSQL | - Monotonic Sequences: Every conversation thread maintains an ordered sequence of events. Each event (message, reaction, membership change, or deletion) receives an incremental sequence integer.
- Deterministic Catch-Up: When a peer reconnects after minutes or days of downtime, it issues a paginated query starting from its stored cursor watermark.
- No Outbound Retry Queues: If a peer is offline when an advisory ping is sent, the ping is safely discarded. No memory is consumed retaining outbound retries because the consumer drives its own catch-up whenever it returns online.
3. Reliable Message Revocation via Tombstones
In a federated system, deleting or recalling a message must propagate reliably across independent databases. If a deletion is treated merely as an ephemeral command or a physical database row purge, an offline peer might miss the deletion notice entirely and retain the message.
In SOMFP, revocations and deletions are sequenced tombstones:
{ "sequence": 2385, "type": "MESSAGE_REVOCATION", "threadId": "thr_01j8abc...", "targetMessageId": "msg_01j8xyz...", "authorNamespace": "alice@node-a.local", "revokedAt": 1731384000, "signature": "3b2e7a..." } - When an author recalls a message or an administrator purges content, a tombstone event is appended to the thread’s feed with a unique sequence number.
- Any peer server consuming the feed encounters the tombstone at sequence
2385before it can process subsequent messages. - The peer's local database atomically deletes the message text and associated media metadata.
- Because the tombstone remains part of the sequential feed, late-joining or temporarily offline servers are guaranteed to execute the purge as soon as they synchronize.
4. Cryptographic Authentication & Domain Continuity
SOMS does not depend on central identity providers or external certificate authorities for peer authorization. Cross-server transactions are verified using Ed25519 HTTP Message Signatures (RFC 9421):
- Root Keypairs: Each server maintains an Ed25519 signing key associated with its public domain name.
- Request Binding: Inter-server requests bind the HTTP method, request target URL, timestamp, and body digest (
content-digest) into a cryptographically signed header. - Public Key Discovery: Key metadata is published under standard
.well-known/soms-federationdocuments. - Key Continuity: Peers cache discovered public keys and enforce cryptographic continuity over time, rejecting requests if an established domain's key rotates without a valid signed key rotation proof.
5. Backend Implementation: Lean Fastify & PostgreSQL
Many self-hosted applications require external dependencies like Redis for caching, RabbitMQ for task queues, and dedicated workers for scheduling. On modest hardware, these components increase memory consumption and configuration complexity.
The SOMS backend is engineered to run as a single, consolidated container alongside PostgreSQL:
+---------------------------------------------------------------------+ | SOMS Node Stack | +---------------------------------------------------------------------+ | Caddy Edge Router | Automated TLS, reverse proxy, static assets | +-----------------------+---------------------------------------------+ | NestJS Core Engine | Fastify HTTP/2, Server-Sent Events (SSE) | | | In-database durable task dispatcher | | | Safe SSRF-isolated link preview fetcher | +-----------------------+---------------------------------------------+ | PostgreSQL Storage | Drizzle ORM, row-level locking discipline | | | Sequential feeds, file metadata, quotas | +---------------------------------------------------------------------+ In-Database Task Coordination
Instead of running a separate message broker:
- Durable Task Queue (
src/durable-task/): Background operations (webhook delivery, federation state fan-out, media cleanup) are stored and tracked directly within PostgreSQL tables. - Concurrency Control: Non-key read-modify-write operations utilize PostgreSQL’s
FOR NO KEY UPDATErow-level lock discipline, preserving concurrency with foreign-key validation and preventing deadlocks during high-throughput message batches. - Crash Recovery: If the host experiences an unexpected reboot or power failure, pending asynchronous tasks resume from their last verified database checkpoint.
The entire server stack idles under 150MB of RAM, making it practical to host on low-power devices and entry-level home servers.
6. Client Architecture: Rust Core & Platform Adapters
SOMS clients are available on Web/PWA, Android, Linux, macOS, and Windows. To avoid duplicating protocol logic, synchronization routines, and cryptographic operations across separate languages, the codebase enforces a strict architectural boundary:
+-------------------------------------------------------------------------+ | Platform Adapters (Thin) | | - Web / PWA: Angular Zoneless Component Library | | - Android: Kotlin Activity, System Notification Bridge, Audio Focus | | - Desktop: Tauri Native Shell (Linux AppImage/deb, macOS DMG, Windows) | +-------------------------------------------------------------------------+ ▲ Raw Facts & Lifecycle │ Explicit Action Commands (Screen state, IPC, │ (Play audio, render notification, network change) │ update native badge) ▼ +-------------------------------------------------------------------------+ | Shared Rust Core | | - Connection lifecycle & exponential reconnect state machine | | - Feed interpretation & cursor validation | | - Secure credential storage & session epochs | | - Encrypted local cache management | | - WebRTC call signaling state machine | +-------------------------------------------------------------------------+ Architectural Principles:
- Rust Owns Business Rules: Connection backoffs, session revocation epochs, message sync ordering, and WebRTC signaling logic are written once in Rust (
src-tauri/src/). - Adapters Report Facts: The platform layer (e.g. Kotlin on Android) does not maintain independent business state machines. It observes system facts (e.g., network online/offline, notification clicked) and sends them to the Rust core.
- Rust Issues Explicit Commands: The Rust runtime computes state changes and commands the adapter to perform OS-level actions (e.g., dispatch an Android system notification, update a dock badge, route audio tracks).
- Non-Blocking UI: Platform calls never block the main thread. Asynchronous work runs inside a Tokio runtime, maintaining responsive 60/120fps UI performance on desktop and mobile.
7. Interactive In-Browser Demo
To make evaluating the client interface fast and transparent, SOMS includes an interactive simulation that runs entirely in the browser without installing software or creating an account.
The SOMS Playground runs an in-memory client engine demonstrating conversation threads, multi-sender group flows, media streaming, and calling interfaces:
👉 Launch the Live SOMS Playground
(Runs purely in local browser memory; no backend setup required)
For self-hosters ready to launch an instance, the playground also includes an interactive Compose file generator that configures domain names, ports, and credentials into a single ready-to-run file:
👉 Docker Compose Configuration Generator
8. Project Status and Links
SOMS is currently in Technical Preview (v0.1.0-alpha.2), tracking the formal SOMFP v1.0.0-draft.2 federation specification.
Available downloads include:
- Web / PWA: Deployable via Docker or static web hosting
- Android: Direct APK packages with native calling and FCM/local notification support
- Desktop: Linux (AppImage & deb), macOS (Universal DMG for Apple Silicon & Intel), Windows (NSIS installer)
Code, documentation, and protocol contracts are available on GitHub:
- 💻 GitHub Repository: hapood/simple-open-messaging-service
- 📜 SOMFP Protocol Specification: Protocol Draft & Schema
- 🌐 Project Homepage: hapood.github.io/simple-open-messaging-service
![Industrial Communication — Ch.10: Factory Automation [EN]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft1hw2kvh64lkchmsurzd.png)

