Back to projects
Realtime Systems Experimental 2026

Super Secret Chat

Realtime chat over Socket.io with rooms, message edit/delete, and live presence. Dockerised from day one so dev and prod stayed identical.

Dockerised realtime messaging

  • TypeScript
  • React
  • Node.js
  • Socket.io
  • Docker

Why I built this

I’d built a lot of features that used real-time without actually implementing the websocket side myself. Pusher, Firebase, Supabase Realtime. They all hide the lifecycle, the room model, the broadcast logic.

This was the smallest project I could come up with that exercises all of that without becoming a Slack clone.

What it does

  • Rooms. Users join a named room and only see messages from that room.
  • Edit and delete on messages. When someone edits, everyone in the room sees the change and a small “edited” marker.
  • Presence notifications when someone joins, leaves, or edits.
  • docker-compose up --build runs the same setup the production deploy uses.

Architecture

There’s one Express + Socket.io process acting as the gateway. State lives in memory. The room registry and the message log are JavaScript objects, not a database. When a client reconnects, the gateway re-emits the current room state so they catch up.

Technical decisions

Socket.io over a raw WebSocket. Reconnection, room semantics, and event-based dispatch are all built in. A raw ws server would have been a couple hundred lines of plumbing for the same thing.

In-memory state. The first version doesn’t need durability. When the server restarts, room history clears. Fine for a prototype.

Docker first. docker-compose up --build is the only way I run it. There’s no separate “for prod” config.

Mutations are broadcast as patches. Edits and deletes send a patch instead of a replacement. Everyone sees the same view of history including the edited marker.

What I learned

Socket rooms aren’t really a primitive. They’re a convention built on top of namespaced emit calls. Knowing that early made the room registry a five-line map instead of something more complicated.

Reconnection is what reveals weak invariants. If a client drops mid-edit, every assumption you made about ordering shows up.

“Edits broadcast to everyone” is a product decision, not a technical one. Worth saying that out loud somewhere.

Next steps

  • Persist rooms and messages to Postgres, or SQLite plus Litestream for the small case. So history survives a restart.
  • Authentication via passkey or magic link. Right now anyone with the URL can join any room.
  • Optional E2E encryption for room contents. Signal-style ratchet, with the gateway as a blind relay.
  • Pull the realtime client into a small npm package. The reconnection and snapshot replay logic is reusable.

Source: github.com/uzairali19/super-secret-chat-app.