Doorlist
A conference web app that lets attendees check themselves in on their own phones, then carries their pass, meal and t-shirt collection, raffle and feedback in the same place.
- Node.js 24
- TypeScript
- Express 5
- PostgreSQL 16
- Redis 8
- Docker
- Cloudflare Tunnel
Built pro bono for a one-day youth conference on 17 October 2026.
In short
- Problem
- Hundreds of attendees queued at one door while volunteers read names off a list.
- Approach
- Move check-in onto each attendee's own phone, and keep passes, item collection and the prize draw in the same place.
- Solution
- Doorlist, one web app for 500 participants and 200 volunteers, built to keep working on a crowded venue network.
At a glance
- participants at a one-day conference
- 500
- volunteers using the same app
- 200
- the room's size, served comfortably in load tests
- 8×
- findings from a security audit, closed in code
- 14 of 15
The problem
Checking in hundreds of young people at a single door means long queues and a crowd of volunteers doing nothing but reading names off a list. Once inside, attendees still had to collect the t-shirts, meals and drinks they had paid for in advance, and the organisers needed to know who had already collected what.
The tools the organisers already used covered parts of this, but each separately, and none of them took the queue away.
The solution
Doorlist moves check-in onto the attendee's own phone. Before the event, every participant and volunteer receives a personal ticket code by WhatsApp. On the day, they enter it, confirm the last four digits of their phone number and scan the poster at the venue. The app then gives them a digital pass with their arrival number.
The same app serves everyone else in the room. Volunteers scan passes to hand over the items each attendee is entitled to, and run a live prize draw from the people who have checked in. Organisers get a door lookup for anyone who needs help, a roster they can edit as people arrive, and a live dashboard of the event.
Key features
-
Self check-in from any phone
Three ways in — a typed code, a personal link or the venue poster — all leading to the same resumable flow. Progress survives a reload, and a completed check-in follows the attendee to a new device.
Why it matters: no queue at the door, and far fewer volunteers needed to staff it.
-
A pass that gives away its own screenshot
Every 30 seconds, the attendee's pass and the volunteer's screen show the same colour and word, derived independently from a shared secret. Neither side asks the other what to show.
Why it matters: a pass forwarded to a friend is visibly out of date, without the volunteer looking anything up.
-
A collection counter that cannot rewrite history
Volunteers scan a pass, see what the attendee is entitled to and has already collected, and slide to confirm. Every outcome is recorded, and corrections are added as new records rather than edits.
Why it matters: no t-shirt is claimed twice, and every handover can be traced afterwards.
-
Built for a room on one network
The limits that stop code guessing are keyed on the attendee rather than the network address, the server turns away excess load instead of slowing down, and a repeated tap is answered without doing the work twice.
Why it matters: hundreds of phones on the venue Wi-Fi share one address, and a tired attendee taps again when a screen is slow.
-
One app instead of several
Ticket delivery, check-in, passes, item collection, the prize draw, a venue map and an anonymous feedback questionnaire all live in the same app, reached from the attendee's pass.
Why it matters: attendees and volunteers learn one tool, and the organisers see the whole event in one place.
Under the hood
For technical readers: the architecture and the decisions behind it.
Architecture and technical decisions
Architecture
One Express app, server-rendered with progressive enhancement, running in Docker alongside PostgreSQL and Redis. Nothing on the host listens on a public port: public traffic arrives through an outbound Cloudflare Tunnel, and the admin surface is reachable only over Tailscale or behind Cloudflare Access, which the app verifies again itself.
| Service | Networks | Reachable from |
|---|---|---|
| Cloudflare Tunnel | edge | Outbound only |
| Tailscale sidecar | edge | Tailnet devices |
| Caddy | edge, web | Tunnel and sidecar |
| App | web, data | Caddy |
| PostgreSQL, Redis | data (internal) | App only |
Decisions
- Least-privilege database roles. Separate roles for the app, migrations and read-only exports, with column-level grants. The app can add to the collection record but cannot read or change it, which makes the audit trail append-only by construction rather than by convention.
- Capacity measured, not assumed. A k6 runner steps load up until the server breaks, then narrows in on the limit. Runs on a busy or throttled machine are discarded. The event host handled 4,000 simulated users comfortably and stayed correct up to 10,000 by turning away the excess.
- Self-hosted telemetry. Attendee data may not leave the host, so counters, event logs, alert rules and a live dashboard are built in rather than sent to a hosted service.
- No backend build step. TypeScript runs directly on Node 24, so there is no compiled output that can drift from the source.
- Private by default on the admin side. Attendee details travel in request bodies, never in page addresses, so they never reach browser history on a volunteer's phone.