Back to Home
Engineering

Architecture

Three maps of Railgun as it exists in the repositories, not as it is pitched: which products talk to which services, what data is stored where, and for each kind of data who can read it. Planned pieces are drawn dashed. Every statement here was checked against the code on 2026-08-30; when the code changes, this page has to.

This is a description, not an audit. The cryptographic detail behind the message rows is on /encryption; the operational controls are on /security; what is collected and why is on /privacy.

1. Product map

One NestJS service, railgun-api on Fly, is the identity authority for every product: it issues the JWT, mints the rg_sso cookie that railgun.chat sets HttpOnly for .railgun.chat, relays messages over Socket.IO, and speaks SMTP for Mail. Meet is the exception: its control plane (tenants, wallets, rooms, usage) is a set of routes inside railgun-site with its own Prisma database, and the meeting app on meet.railgun.chat calls it for room tokens. Teal outlines are things you can use today; dashed grey is planned; blue is a third party that processes data on Railgun's behalf.

Rendering diagram…

Text version of this diagram
  • Clients: the railgun.chat site and its /app surface, the Electron desktop app, iOS and Android; a browser chat client is in build. All authenticate against railgun-api — the site via the rg_sso cookie and a JWT, the native apps with sealed-box DM envelopes and, on desktop and iOS, sender-key group envelopes.
  • Products available now: Mail (served by railgun-api), Meet (rooms and persistent voice channels) and Support/Tickets in private beta. Planned: Privacy, JobClaw, Node Mode, Sign, Storage and Pi.
  • Railgun infrastructure: railgun-api on Fly writes users, devices, public prekeys and mail rows to Supabase Postgres (schema railgun_api), queues offline envelopes in Redis for 30 days, stores client-encrypted attachment blobs on its disk, submits outbound mail to Resend over SMTP and pushes mail.new notifications through Supabase Realtime. The Meet control plane inside railgun-site keeps tenants, wallets, rooms and usage in its own Prisma Postgres and mints LiveKit tokens and Deepgram grants.
  • Third parties: Vercel hosts the site and Meet; LiveKit Cloud carries DTLS-SRTP media; Deepgram produces captions from audio; DeepL translates caption text; Anthropic turns a transcript into minutes without storing it; Resend relays external mail.

2. Data architecture

The relay keeps no message table. Migration DropMessageTables1740900000000 removed messages and message_envelopes, and nothing recreated them. A message is delivered over the socket if the recipient device is online; otherwise its envelope waits in a Redis list under a 30-day TTL and is deleted on acknowledgement. Your device holds the only copy of your history.

Postgres, in the railgun_api schema of a Supabase project, holds accounts and the material needed to find and address people: users, handles and handle reservations, invites, devices, and the public halves of identity keys, prekeys and signed prekeys. Sender-key distributions are stored too, but each one is a sealed box addressed to a specific recipient device, so the row is opaque to the server. Attachment rows hold metadata only; the blob itself is client-side ciphertext written to the API machine's local disk.

Mail is the one product whose content the server holds. Inbound external mail is encrypted at rest with AES-256-GCM under a key derived from a server secret. Internal and outbound mail is base64-encoded and not encrypted, even though the column is named encryptedBody. Meet's control plane is a separate Prisma database of tenants, wallets, rooms, usage events and embed keys; it stores no media, captions or transcripts.

Rendering diagram…

Text version of this diagram
  • Your device: private identity key, sender-key chains and message history; the only copy of the history.
  • Postgres (railgun_api): users, handles, invites, devices with argon2id password hashes and HMAC recovery codes; public identity keys and prekeys; sender-key distributions sealed to each recipient device; attachment metadata (size, ciphertext SHA-256, key commitment); mail_messages and mail_addresses where inbound external mail is AES-256-GCM under a server key and internal/outbound mail is base64. There is no messages table.
  • Redis: offline envelope queue, ciphertext only, 30-day TTL.
  • API disk: attachment blobs as XChaCha20-Poly1305 ciphertext.
  • Meet control plane: a separate Postgres with Tenant, Wallet, Room, UsageEvent and EmbedKey; no media, captions or transcripts.

3. How privacy is maintained — and where it isn't yet

DataMechanismWhat the server seesLimits todayStatus
Direct messageslibsodium sealed box (X25519 + XSalsa20-Poly1305) to each recipient device’s identity key. Desktop, iOS, Android and the browser client share one wire format (protocolVersion 2).Ciphertext, sender, recipient, timestamps.No forward secrecy and no post-compromise recovery: a leaked device key opens every past message sealed to it.End-to-end
Group & channel messagesSender keys: each sending device owns a per-group chain key, ratcheted with BLAKE2b per message; distributions are sealed-boxed to every member device and re-issued when membership changes (protocolVersion 3).Ciphertext, group id, sender, counter and epoch. Sealed distributions it cannot open.Shipped in the desktop and iOS clients. Android still sends DMs only; the web chat client is in build.End-to-end
AttachmentsXChaCha20-Poly1305 per file on the client; filename and thumbnail encrypted under the same key; the key travels only inside the E2E message payload.Ciphertext blob, its SHA-256, size, MIME type, a BLAKE2b commitment to the key, and which conversation it belongs to.Desktop and iOS. Blobs sit on the API machine’s local disk with no volume mount, so they are not durable across redeploys.End-to-end
MailNot end-to-end encrypted. Inbound external mail is AES-256-GCM at rest under a key derived from a server secret; Railgun-to-Railgun and outbound mail is stored base64-encoded, which is encoding, not encryption.Everything: sender, recipients, subject, body, attachments.Tracker-free on receipt: pixels, beacons and redirectors are stripped from inbound mail, and Railgun-to-Railgun mail is never tracked. Pro senders can enable open/click tracking on mail to external recipients.Server-readable
MeetMedia is DTLS-SRTP between your browser and LiveKit Cloud (no end-to-end encryption). Captions stream audio to Deepgram under a short-lived grant; captions are translated by DeepL; minutes are generated once by Anthropic from the transcript in your browser.Tenant, wallet, room and usage rows in the control plane. No media, captions or transcripts are written anywhere.LiveKit and the three AI vendors necessarily process the content in the clear while a call runs. An E2EE key provider exists in code and is not wired.Transit-encrypted
IdentityHandle + password + invite code. Email is optional; no phone number is collected. Passwords hashed with argon2id; ten recovery codes stored as salted HMAC-SHA256 and shown once; refresh tokens rotated and stored hashed.Handle, display name, device list, public keys, invite lineage, session and audit rows.Invite-only while the handle namespace is being reserved; the relay is the single identity authority, there is no third-party auth provider.Minimal

An operator with database access

Someone holding Railgun's Postgres, Redis and server secrets — an employee, a subpoena, or an attacker who took the machine — gets exactly this.

Can read

  • Who has an account: handles, display names, optional email, device names and when each device was last seen.
  • Who talks to whom: DM conversation pairs, channel and group membership, and how many envelopes are waiting in a queue.
  • Every mail message, in full — plus the server-side key for inbound external mail, which lives on the same machines.
  • Attachment metadata: size, MIME type, ciphertext hash, which conversation, and when it was uploaded.
  • Meet tenants, wallet balances, room slugs and usage metering.

Cannot read

  • The text of any direct, group or channel message — no plaintext or key ever reaches the relay, and no message table exists to search.
  • The contents, filename or thumbnail of an attachment.
  • Any private identity key, sender-key chain or the ephemeral keys inside a sealed box.
  • A password or recovery code in the clear.
  • Meet audio, video, captions or minutes: nothing is persisted, and LiveKit media does not transit Railgun servers.

The honest one-line summary: messages and files are private from Railgun; mail and metadata are not. Closing the remaining gaps — forward secrecy for DMs, group encryption on Android and the web, end-to-end mail between Railgun accounts, LiveKit end-to-end encryption — is tracked on the build plan.