Building in public

The Railgun Build Log

Releases, trust fixes, engineering notes, and honest progress on the 2026 plan. No claim before the implementation supports it.

trustsecurityi18nshipping

We audited our own claims and took several of them down

The rule on this page is that no claim ships before the implementation supports it. We ran that rule against our own site this week and it cost us. Several claims did not survive. Here is what changed and why.

The homepage advertised cryptography our client does not perform

Until today the homepage carried three cards: X3DH + Double Ratchet, Curve25519, and ChaCha20-Poly1305. They are gone.

We checked them against the shipping desktop client rather than against our own documentation, and the documentation was ahead of the code:

  • Channel and community messages are not encrypted on any code path. One path returns the message base64-encoded behind a literal [DEVCRYPTO:PLAINTEXT] marker; the other logs Channel encryption not yet implemented and returns base64 of the plaintext. Base64 is an encoding, not a cipher.
  • Direct messages on the Electron path are not encrypted either. The handler is commented Placeholder encryption and returns the plaintext, base64-encoded.
  • The real Signal Protocol implementation exists and is never used. RailGunCrypto.ts contains genuine X3DH and Double Ratchet code. It is imported as a type only; the runtime is wired to a development shim. It has been dead code in the shipping path.
  • Direct messages on the other path are genuinely encrypted — libsodium sealed boxes, X25519 with XSalsa20-Poly1305. So "Curve25519" was defensible. "ChaCha20-Poly1305" is a different cipher, and "X3DH + Double Ratchet" was not running.

Every one of those placeholders was honestly commented in the source. One of them says, in as many words, that it is "intentionally obvious so no one mistakes it for real encryption." Nobody was hiding this. The gap was between what the code said about itself and what our marketing said about the code, and that gap is our fault, not the code's.

Railgun began as a Signal-style chat app and has since become something broader. Those promises came along unexamined. They are off the site until the implementation is real.

What we did instead of quietly deleting them: we published a failing test suite that defines what "encrypted" has to mean — that base64-decoding a message must not recover it, on every path, for direct and channel messages, and that decrypting with the wrong key must fail rather than succeed. Thirty tests. Nineteen fail today. They are the acceptance criteria for the work, and the net that stops it silently regressing. Six of them already pass, on the one path that is real, which is how we know the bar is reachable and not theatre.

Railgun Mail cannot currently send to the outside world

This is an outage, not a wording problem, and we found it by checking our own DNS rather than our own README.

Our SPF record authorises a single IP address and ends in a hard fail. The machine that actually relays mail is a different host, and it is not in that record. The authorised address has a reverse-DNS name pointing at a third machine again, so forward-confirmed reverse DNS cannot pass either. Whichever host sends, SPF fails.

/mail now says so, in a status block near the top, and the page has been rewritten so that nothing on it implies you can send mail out today. Receiving is a separate path and is unaffected.

We have also stated the encryption position on that page exactly: inbound external mail is encrypted at rest with AES-256-GCM, but under a key the server derives and can therefore use. Mail composed inside Railgun is stored base64-encoded, which is not encryption at all. Nothing in Railgun Mail is end-to-end encrypted today, and the page now says that in those words.

Two corrections on Railgun Pixel

The page claimed Railgun's own tracking domains were on the extension's denylist. They were not — there are zero Railgun entries in it.

The interesting part is that there was a true version of the claim worth keeping. Railgun's own read receipt is a 1×1 hidden image, so the extension's structural checks do catch it and show you the host. What it does not do is name Railgun, because we are not in the curated vendor list. The page now says exactly that, and commits to changing it when that changes.

We also removed a "1 free tracking view per month" claim that was impossible twice over: free senders never receive a tracking ID, and the endpoint refuses the request before it runs a query. Replaced with the actual free tier. And "hundreds more" tracking services became the real number, which is 54.

The site now genuinely speaks 20 languages

It has offered 20 for a while. Choosing one navigated you to the right URL and then rendered English, because only two of sixty-nine pages and six of twenty-one components ever called a translation.

That is fixed. The homepage and its components are extracted and translated, page metadata is localised across all sixty-nine pages, and every locale file is verified at exact key parity on each build — 490 keys, twenty files, no silent drift.

The language menu now leads with English, Spanish and Portuguese, then orders the rest by number of speakers worldwide.

Security fixes

  • Cross-site scripting in the mail reader. Sender-controlled HTML was assigned to innerHTML on a live document, so an image error handler in a hostile email would run without any interaction. Now parsed into an inert document where scripts cannot execute and resources never load.
  • An open proxy. A health-check endpoint took both the path and the HTTP method from the query string, with the restriction stated only in a comment and enforced nowhere. Anyone could make railgun.chat issue arbitrary requests to our production API from our own egress addresses. Now restricted to the fifteen paths the status page actually probes.
  • Admin authorization. Only one of twelve privileged actions checked the operator's role; the rest checked merely that someone was signed in. All twelve now declare the privilege they require, through a helper that fails closed — an action that forgets to declare one does not compile. Three further holes turned up in the process, including one that let an admin promote themselves to owner.
  • Sign-out did not sign anyone out. Sessions were stateless tokens with no revocation, so a stolen one stayed valid for a week. Signing out now ends every session on every device, and changing a password invalidates outstanding ones.
  • Abuse controls. Our waitlist endpoint would send Railgun-branded email to any address supplied to it, unauthenticated. Our translation demo had no character budget despite a comment claiming one, and could have exhausted a month of translation quota from a single address in two minutes. Both are closed.

Also shipped

A rewritten homepage introduction, an llms.txt so AI answer engines describe us from a checked document rather than inferring capability from marketing language, a robots policy that takes a deliberate position on AI crawlers instead of an accidental one, sitemap and canonical fixes, and structured data that no longer points at a repository we do not own.


None of this is the week we planned. It is the week the audit gave us. We would rather publish a shorter list of true claims than a longer list we cannot defend, and we would rather you read it here than discover it yourselves.

The encryption work is next, and the tests are already written.

trustsecuritywebsite

Trust sprint: Google Analytics removed, real CSP shipped

Two fixes from the plan's trust sprint shipped today:

Google Analytics is gone. A privacy company shipping unconsented third-party tracking on its own website is not credible. The site now ships zero third-party analytics. If we add analytics later, it will be a cookieless, privacy-respecting option — disclosed on the trust page.

A real Content-Security-Policy is live. Previously the site claimed CSP protection without deploying one. Now every response carries a policy that allows no external script origins at all, plus HSTS with a two-year max-age. You can verify this yourself with curl -I https://railgun.chat — and we'd encourage you to.

This is the pattern for everything that follows: no claim goes on a marketing page before the implementation supports it.

plantrust

The 2026 plan is public

Railgun's full 2026 operating plan is now public at plan.railgun.chat — phases, deadlines, decision gates, estimated costs, pricing logic, and the honest privacy boundaries we hold ourselves to.

The short version: broadcast loudly, communicate privately. We build an audience by streaming consistently, we sell Railgun Meet first, and the ultra-private communications platform continues as the long-term moat without being allowed to delay revenue.

There's also a live progress tracker showing every phase, task, and gate — updated as work actually lands, not as we wish it had.

phase-completeshippingtrust

Phase 0 complete: plan.railgun.chat, progress tracker, Build Log, GitHub org setup

Phase 0 is done. Everything that was promised for "inventory and truth" shipped today:

The canonical master plan. plan.railgun.chat is the source of truth for Railgun through December 31, 2026. Every phase, every gate, every cost estimate, and the three honest privacy boundaries are there. The plan itself is a Markdown file that lives in source control — no duplicate website copy to drift.

Interactive progress tracker. plan.railgun.chat/progress shows every phase with expandable detail cards: decision gates, cost ranges (Fable-class AI tokens + direct cash), per-task checklists, and a timeline bar with zoom controls. The page is built from a single typed data module — when a task lands, a one-line flag change updates the tracker everywhere.

The Build Log. updates.railgun.chat is where we publish shipping announcements, trust fixes, and honest progress notes. It's file-based (one Markdown file per entry) so posting is as simple as committing a new file to content/build-log/. Later, entries can be auto-drafted from GitHub Releases.

GitHub organization checklist. docs/engineering/GITHUB-ORG-SETUP.md is the Phase 1 execution checklist covering org controls (2FA/passkeys/secret scanning), team structure with least privilege, per-repo baseline, branch protection including a 2-reviews-with-Security rule for crypto paths, and the initial monorepo shapes for railgun-platform, railgun-meet, and railgun-infra.

Trust fixes shipped. Google Analytics removed, real CSP + HSTS headers live. No external script origins allowed at all. Verified with curl.

Three new private repos created: railgun-platform (identity, billing, metering), railgun-meet (the revenue product), railgun-infra (deployment and runbooks).

The gate for Phase 0 was "no new privacy marketing claims until source and deployment reality are mapped." We've mapped it, published it, and shipped the fixes we found. Phase 1 starts August 3: the trust surfaces, uptime monitoring, streaming stack, and the first broadcasts.