# `rate-limiter.ts` ## Purpose `AdminRateLimiter` enforces the design §8.4 per-principal rate limits on every admin endpoint. Read endpoints share a 60-calls-per-minute bucket; destructive endpoints share a 1-per-minute bucket. Both buckets are scoped per principal id so a misbehaving operator cannot starve the host of admin throughput for everyone else. The limiter is a thin wrapper around `@suaveplan/rate-limiter`'s `TokenBucket` — one bucket per `(principalId, kind)` pair, allocated lazily on first contact. The refill rate is set so the bucket starts full and drains to zero after `capacity` consecutive calls in a minute; refill is linear, so a steady stream is permitted at the documented rate. Bursts up to `capacity` are allowed. ## Features - Per-principal isolation — one bucket pair per id. - Lazy allocation — principals that never hit admin are zero-cost. - Token-bucket refill maps `capacity` calls to one minute. - Injectable clock for deterministic tests. ## Usage: Basic ```ts import { AdminRateLimiter } from "@suaveplan/api-host/admin-api"; const limiter = new AdminRateLimiter(60, 1); limiter.consume("alice", "read"); // true limiter.consume("alice", "destructive"); // true limiter.consume("alice", "destructive"); // false (until refill) ``` ## Usage: Advanced ```ts let now = 0; const limiter = new AdminRateLimiter(60, 1, () => now); limiter.consume("alice", "destructive"); // true limiter.consume("alice", "destructive"); // false now = 60_001; limiter.consume("alice", "destructive"); // true — refilled ``` ## API Reference - `new AdminRateLimiter(readPerMinute, destructivePerMinute, clock?)` - `consume(principalId, kind): boolean` — `true` on success, `false` when rate-limited. ## Implementation Notes `TokenBucket`'s `consume(1)` updates the bucket's internal counter and returns a `RateLimitResult`. The admin API only cares about the `allowed` boolean — it surfaces `429` + `Retry-After` purely from constants when `consume` returns `false`. The principal-id key is the rate-limit cardinality bound. A deployment with many distinct admin principals carries a small memory cost (~80 bytes per bucket pair) — gateways should rate-limit admin traffic at the edge before it reaches the host if extreme multi-tenant cardinality is anticipated.