Starter template (TypeScript) for the Rate Limiter primitive on karnstack.
Five stages. Paper- and engineering-backed tests. You implement the interface; karnstack tells you what to read at each stage.
mise is the only thing you need installed globally. It pins Node 24 and pnpm 10 for this repo.
Install mise:
curl https://mise.run | shmise trust # allow this repo's .mise.toml (one time)
mise install # installs Node 24 + pnpm 10
mise run setup # installs npm dependencies
mise run stage 1 # runs the tests for stage 1 (they fail until you implement)Open stage 1 on karnstack. Implement src/ratelimit.ts until mise run stage 1 passes. Then move on:
mise run stage 2mise run all runs every stage in one go.
.
├── .mise.toml # toolchain + tasks
├── package.json
├── tsconfig.json
├── src/
│ └── ratelimit.ts # you implement here
└── tests/
├── _clock.ts # FakeClock helper
├── stage01.token-bucket.test.ts
├── stage02.sliding-window.test.ts
├── stage03.counter.test.ts
├── stage04.burst-retry-after.test.ts
└── stage05.jitter.test.ts
- Token bucket (single-process)
- Sliding-window counter behind a shared Limiter interface
- Pluggable Counter backend (in-memory; Redis Lua documented)
- Burst budget and Retry-After math
- Client jitter strategies (full, equal, decorrelated)
Each stage is described on karnstack. Read first, then implement.
A rate limiter with two interchangeable algorithms behind a single interface, a swappable counter backend so multi-process production wiring is a constructor change, exact and conservative Retry-After math, and the three client-side jitter strategies that prevent the next thundering-herd.
- IETF (1999). RFC 2698: A Two Rate Three Color Marker.
- IETF (2022). RFC 9110: HTTP Semantics, section 10.2.3 Retry-After.
- Brooker, M. (2015). Exponential Backoff and Jitter. AWS Architecture Blog.
- Cloudflare (2017). How we built rate limiting capable of scaling to millions of domains.
- Tarjan, P. (2017). Scaling your API with rate limiters. Stripe Engineering Blog.
MIT. See LICENSE. Your fork is yours.