Why We Chose Elixir and Phoenix for Real-Time Privacy
The Requirements
Before writing a single line of code, we outlined exactly what the platform needed: real-time messaging, persistent WebSocket connections, fault tolerance, minimal resource overhead, and the ability to handle many concurrent connections without traditional scaling pain.
The server also had to be deliberately ignorant — it would never see plaintext, never store identifiable data, and never log anything useful to an attacker. This constrained our technology choices significantly.
Why Elixir?
Elixir runs on the BEAM VM — the same runtime that powers Erlang, which was built at Ericsson for telecom systems. A chat application is quite literally what the BEAM was designed for.
The BEAM gives us lightweight processes (not OS threads — each costs only a few kilobytes of memory), preemptive scheduling (no single connection can starve others), and “let it crash” fault tolerance (a bug in one connection does not take down the server).
Each connected user is a separate BEAM process. They are isolated, independently garbage-collected, and supervised. If one crashes, the supervisor restarts it. The rest of the system never notices.
Phoenix Channels
Phoenix provides first-class WebSocket support through its Channels abstraction. Each chat room is a topic, each connected user is a process. The runtime handles millions of connections natively — no Redis pubsub, no external message broker, no additional infrastructure.
Our channel implementation handles message sending, read receipts, typing indicators, message editing, message deletion, and room expiry — all through a single WebSocket connection per user. The protocol is simple, the events are well-defined, and the server logic is auditable.
Why Not Node.js, Go, or Rust?
Node.js
Single-threaded by design. Real-time at scale requires external solutions like Socket.io with Redis adapters. More moving parts, more attack surface.
Go
Excellent for APIs, but WebSocket support requires more manual work. Goroutines are lightweight but lack the supervision tree model that makes Elixir fault-tolerant.
Rust
Unnecessary complexity for this use case. The performance ceiling of Rust is not needed when the BEAM already handles millions of concurrent connections.
Phoenix gives us the best ratio of developer productivity to runtime performance for real-time applications. The framework was purpose-built for exactly this kind of workload.
The Anonymous Socket
Our WebSocket has no session, no auth cookie, no user ID. It is fully anonymous by design. Phoenix makes this trivial — you simply skip the authentication in the socket’s connect callback.
Most frameworks assume you want to identify users on WebSocket connections. Phoenix lets you choose. We chose anonymity, and the framework did not fight us on it.
OTP for Background Jobs
Oban, built on Elixir and OTP, handles our maintenance tasks: every hour it deactivates expired TTL rooms and hard-deletes their messages and (room_hash, access_hash) records in a single transaction. Messages replaced by replies are hard-deleted immediately in the same transaction — no background job needed. The BEAM’s scheduler ensures these background jobs never interfere with real-time chat performance.
LiveView for UI
The entry form and admin dashboard use Phoenix LiveView for server-rendered interactive UI. No JavaScript framework needed for the non-chat pages. The chat itself uses raw Phoenix Channels with plain JavaScript hooks for client-side crypto.
The Result
A single Elixir node handles the full application: HTTP requests, WebSocket connections, background jobs, and database interactions. Simple to deploy, simple to maintain, simple to audit.
No microservices. No message queues. No container orchestration. One binary, one process tree, one codebase. The simplicity is the security — fewer moving parts means fewer places for bugs to hide.
See the Elixir-powered platform in action.
Start a Private Chat