Concert: a FIFO waiting-room reverse proxy that shields fragile origins
A single Go binary queues visitors past your origin's capacity and serves assets, streams and bans around them.
Concert calls itself a FIFO waiting room reverse proxy, and it means exactly that. You put the single Go binary in front of any HTTP origin — a PHP site, WordPress, a legacy app — and it caps how many concurrent page requests reach that origin. Everyone past the cap waits in an orderly queue with a visible position instead of piling into socket buffers until they time out.
The problem it solves is concrete: a PHP-FPM pool has pm.max_children workers, and when every worker is busy new requests sit in the web server's backlog until they 502 or 504. Users refresh, making the spike worse. Concert moves that backlog out of the socket buffer and into a waiting room where people can see their place in line.
It is one Go module (github.com/andreimerlescu/concert, Apache-2.0) built on three libraries by the same author: room for the FIFO waiting-room middleware, sema for resizable semaphores, and the ess package of naddr to tell the admin portal who owns each address it shows.
How traffic is split
Every request first resolves a client address. Banned clients are rejected before anything else runs — a single map lookup that answers with 429 until the cooldown ends (403 for permanent bans).
From there, requests take different paths:
- Pages go through
room. A ticket inside the serving window takes a slot, is proxied, and releases it when the response has been fully delivered. Everyone else gets aroom_ticketcookie and the waiting room page, which polls/queue/statusand reloads when their turn arrives. - Admitted pages also carry
concert_admit, an HttpOnly signed pass that slides forward as the visitor keeps browsing. - Assets don't queue — a stylesheet can't run the waiting room's JavaScript. They require a valid pass, each with its own small concurrency pool so one visitor can't monopolise the tier, plus a global semaphore across all passes to protect the host.
- Streams (WebSocket and SSE) are served outside the room behind their own guards: a valid admission pass and a
-stream-capconnection limit. - Bypass paths go straight through with no pass, queue or semaphore; banned clients are still rejected.
The router in router.go wires these in that exact order, so identification and ban enforcement run first, then ops endpoints, bypass, assets and streams outside the room, then the priority gate and API interceptor, then room's middleware and a gated proxy catch-all. Route conflicts make gin panic; they are returned as errors instead.
Starting it
Install with go install github.com/andreimerlescu/concert@latest, or build from source:
git clone https://github.com/andreimerlescu/concert.git
cd concert
make build
make build writes static binaries for Linux, macOS and Windows on amd64 and arm64 into bin/. It needs Go 1.26 or newer.
A quick start against PHP's built-in server:
php -S 127.0.0.1:3000 -t /var/www/html
concert -upstream http://127.0.0.1:3000 -cap 5 -assets "/css/*,/js/*,/images/*"
Then generate load with ab and refresh the browser to land in the waiting room, then be admitted automatically as slots free.
Every option is a flag or an environment variable; a flag overrides its env var, which overrides the built-in default. Values changed in the admin portal are saved to settings.json and override both. Secrets — CONCERT_ADMIN_TOKEN, CONCERT_ADMIT_SECRET, CONCERT_PORTAL_PASS — are environment-only on purpose, because command-line arguments show up in ps output.
The default -cap is 434 concurrent page requests, with a queue breaker at -max-queue 369. Throughput follows Little's law: admissions per second ≈ cap divided by average time a slot is held. If the origin slows down under load, admissions slow down with it — which is exactly the protection you want.
The abuse registry
Concert keeps an in-memory registry of clients that misbehave. Each act adds weighted strikes; when a client's strikes within -abuse-window reach -abuse-strikes, it is banned for a cooldown that doubles with each repeat ban, up to -abuse-max-cooldown.
What earns strikes:
| Behaviour | Strikes | |---|---| | Admission pass with a bad signature | 5 | | Wrong admin token | 5 | | Per-user asset pool exhausted | 1 | | Queued without a room_ticket cookie | 1 |
With defaults, twenty strikes within one minute trigger a ban — four forged passes, or twenty cookie-less queue arrivals. Missing, expired or stale passes never earn strikes, nor does hitting the global asset limit.
Administrators can also list paths in -ban-paths that no legitimate visitor ever requests (/.env, /phpmyadmin/*, /.git/*) and ban a client on first hit. Range bans cover whole CIDRs — /8 for IPv4 and /16 for IPv6. IPv6 clients are tracked by their /64 prefix because one host can rotate through billions of addresses.
The admin portal
Set CONCERT_PORTAL_PASS (at least 16 characters) and the portal listens on 127.0.0.1:8081, reachable only from CIDRs in -portal-allow. It has six tabs — Overview, Queue, Bans, Visitors, Ban log, Settings. Its Queue, Bans, Visitors and Ban log tables are server-side rendered one page at a time and update every three seconds; Overview shows counters and Settings is a form to change settings live.
Every address shows IP details when the database knows it: country flag, ASN and network description, plus badges for IPv4/IPv6/IPv8 forms. Concert gets this from a local copy of the IPtoASN combined database (NADDR_DATA) or a running naddr service; with neither, it shows bare addresses.
The portal's history keeps every request the access log records, every banned-client request, and each ban — written to history.jsonl, buffered and flushed once a second so requests never wait on disk. The file rotates at 64 MB keeping four older files.
Persistence and shutdown
Concert keeps settings.json, bans.json, queue.snapshot and history.jsonl in -data-dir (default /var/lib/concert/data), created with mode 0600. Bans are restored at startup, the queue is saved on shutdown and restored on start. On SIGTERM it stops accepting connections, gives in-flight requests up to 30 seconds, then saves everything.
On RHEL-family systems, installer.sh installs a prebuilt binary as a confined systemd service under SELinux enforcing mode, downloads the IPtoASN database and schedules its daily refresh.
Honest limitations
The README is candid about what doesn't scale. One instance means one queue, one registry — two instances are two independent waiting rooms, so divide -cap by the number of instances and enable sticky sessions if you must run more than one. A pass limits concurrency, not request rate: fast assets finish quickly, so a single pass can still generate many requests per second; volumetric attacks from many addresses belong at a CDN or WAF in front.
The simple skip-the-line pricing card (-rate, -surge, -skip-url, -pass) is not wired end to end — promoting a paid visitor through it requires calling room's in-process API, which Concert doesn't yet expose. Leave -rate at 0 for now; the portal's "move to the front" works without it.
Separately, the repository ships a fast-lane payment feature documented in docs/FASTLANE.md: a built-in chain gateway (internal/gateway) that verifies and settles payments, plus a reference client. Concert sells time-limited passes with x402 exact on XRPL, Stellar and Hedera, or admits verified NFT holders, with native Solana supported through a concert-native-sol extension rather than the standard SPL-token scheme. That path is wired end to end — the browser pays through /_concert/access, settlement is journaled under -data-dir, and paid requests use the priority lane.
The test suite runs the real waiting room, asset tier, abuse registry, portal and history against a fake origin — covering configuration precedence, pass signing and tampering, per-user HTTP/1.1 and HTTP/2 limits, every strike source, IPv6 /64 grouping, range bans, queueing for browsers and API clients, priority grants, and the history log across restarts.
Development
make all # vet, clean, test, race tests, benchmarks, cross-platform build
go test -race -count=1 ./...
The Makefile also has a summary target that runs the summarize tool with -s and -x flags. Concert is at release v0.0.8 with its last commit in October 2026.
andreimerlescu/concert: 4 MB in 137 files, indexed Nov 9, 2026 after 0.00 hours of indexing. License: Apache-2.0. Source: https://github.com/andreimerlescu/concert.
Ask your agent to name it in its tool calls (the repo argument of query_graph, recall_project and read_project_source).
Written by AI models on the operator's own hardware, checked by a second model, from the repository's README and its code. Back to the shelf