ParleyRealtime Chat
What it does for you
Slack-lite team chat with rooms, DMs, threads and presence over a hand-written WebSocket server, zero npm.

Screenshots

In more detail
Parley is a small Slack-style team chat. Messages, typing, reactions, and who is online show up for everyone instantly. It has public and private rooms, DMs, threads, @mentions with an inbox, unread badges, and search within a room.
Honest limits
History and search are rebuilt in memory at boot. That's fine for a team, not for years of messages. Browser push notifications and file uploads were cut for time. The demo runs three simulated teammates on a fictional team. It's never been under real load.
How I built it
This was another row from Codex's list that Claude Code took over on the final day. I set the spec and the order: pure core, then tests, then server, then UI. Claude Code wrote it all in small files on Node 24 with node:http and built-in node:sqlite, vanilla ES modules in the browser, no npm, no build step.
Realtime apps look simple until the network misbehaves: a dropped socket, a retried send, a tab that was asleep for a minute. So I wanted to prove the parts that make chat trustworthy (no duplicates, no missed messages, no leaks from private rooms), not just a demo on a perfect connection.
Accounts use scrypt password hashes and httpOnly session cookies, and the WebSocket only opens if the session cookie checks out during the upgrade. A live test boots the server on a spare port, opens real WebSocket connections with cookies, and proves delivery, typing, private-room isolation, retry, rate limiting, and reconnect replay. Then Claude drove headless Chrome over the DevTools protocol to click through the real UI: send, autocomplete, drop and reconnect, thread reply, search. That run caught a bug every unit test missed.
What I learned
- One global change counter (
rev) that every post, edit, delete, and reaction bumps solves three problems at once. Reconnect replay is "everything with a higherrevin rooms you belong to," stale updates get dropped, and a client-generated id makes sends safe to retry. - The reconnect cursor has to start at the server's
revfrom the welcome message. We only advanced it on live events, so a quiet client reconnected with no cursor and would have missed everything. The server tests were all green. Only the browser run caught it. - Check membership when each event goes out, not when the socket connects. Invites, joins, and DMs change membership while sockets stay open.
- A presence grace window is a state-machine problem, not a timer problem. With a fake clock, the flicker cases (second tab, quick reconnect, heartbeat timeout) became plain assertions.
- Safe Markdown was easier by escaping everything first, then letting the renderer produce only a short whitelist of tags, with link URLs checked against an allowlist.
What carried forward
We didn't write a WebSocket layer from scratch. The handshake, frame codec, SHA-1, parser, and rate limiter are imported by relative path from project 91 (the multiplayer game, Veer), which was built earlier that day with its own tested core/ and server/ws.js. That's the reuse pattern paying off: a hard protocol piece got written once and trusted twice. The browser-driven click test over the DevTools protocol is a pattern that also shows up across the RAG builds (64, 65, 66) and the bookkeeping app (67).
If I took it further
I'd keep the pure core and the rev design, but move history and search into SQL queries so it scales past a small team. Then I'd add push notifications and file uploads.
#063 Built with Claude Code September 24, 2026 446 automated checks