Case · Backend · TypeScript
poll-board: real-time polls
A polling service in TypeScript with no runtime dependencies. A poll of 2 to 10 options with a lifetime, a link, results growing for everyone in real time over SSE or WebSocket (WebSocket frames are parsed by hand). Repeat votes and stuffing are cut by a signed token and an IP hash; the CSV export is protected from formula cells.
- TypeScript
- Node.js 24
- SSE
- WebSocket
- fast-check
- vitest
- axe
- Docker
- GitHub Actions
01 · How it looks
Screenshots


02 · About
What it is and why
A polling service in TypeScript with no runtime dependencies. A poll of 2 to 10 options with a lifetime, a link, results growing for everyone in real time over SSE or WebSocket (WebSocket frames are parsed by hand). Repeat votes and stuffing are cut by a signed token and an IP hash; the CSV export is protected from formula cells.
Limitations
Rate limits and stream counters live in the memory of one process.
The poll author is not authenticated, only a close key.
A cookie token does not stop someone who clears cookies and changes IP.
A 244 MB image on node:24-alpine; the project runs on a small VPS with demo data.
What is inside
- SSE and WebSocket over one event stream; order is monotonic: seq grows by 1, closed comes last
- 43 tests; fast-check: the vote total equals the accepted ballots, WebSocket frames under any stream chunking
- 200 requests (20 in parallel): p50 6.4 ms, p95 21.7 ms; about 25 MiB RAM
- Run read-only, cap-drop ALL, uid 10001; axe 0 in Firefox at 390 and 1280
03 · How to run
Run it locally
git clone <repository on request> poll-board
cd poll-board
npm ci && npm run build && npm start04 · Code
The repository is private
The source is in a private repository. Message me on Telegram and I will open access.
Request the code