A gateway for teams sharing one LLM API key: everyone gets a personal key and a budget, the real key never leaves the gateway. That only works if the limit is a limit, and rate limiting per replica is quietly wrong. The admission-control code below is the gateway's own, the same algorithms its Redis Lua scripts implement, compiled to WebAssembly on a virtual clock.
measured against the kind cluster with k6 · docs/ratelimit-proof.md · suites in loadtest/
Press the button. One request, one token out of the shared bucket. When the bucket is empty the limiter stops it and nothing reaches the upstream.
Fig. 1. This is not a simulation. The button calls a tollgate gateway
running on Railway, which checks one token bucket in Redis and then proxies to a demo
upstream. The bucket holds 12 and refills at 1 a second, and it is the same bucket for every
visitor: if the number drops without you touching anything, that was somebody else. A 429
here is the gateway's real answer, carrying the real Retry-After.
The service sleeps when nobody is using it, so the first request after a quiet spell
takes a few seconds to wake it.
Fig. 2. Admitted requests per second over 30 simulated seconds. The dotted line is the offered load; the fine rule is the policy. The dashed gateway lets every replica count on its own, so each one happily admits a full ceiling; the solid gateway makes every replica check one shared store before admitting, the exact check the Redis Lua scripts perform in production. The solid line's first second sits above the ceiling by design: that's the token bucket honoring its configured burst, and the policy bound accounts for it.
| requests offered | – |
| policy bound for the horizon (ceiling × 30s + burst) | – |
| admitted, per-replica counters | – |
| admitted, one shared store | – |
| over-admission, per-replica | – |
| requests admitted above the policy bound | – |
| at $0.002 per request (illustrative LLM call) | – |
| same leak, extrapolated to a day at this load | – |
running…
internal/ratelimit/memory.gointernal/ratelimit/tokenbucket.lua,
internal/ratelimit/slidingwindow.luainternal/ratelimit/clock.go,
cmd/demo-wasm/main.gointernal/proxy, internal/resilience