/** * Per-node in-memory token-bucket rate limiter (#163). * * Deliberately PER-NODE, not replicated: Harper replication makes a shared * counter table a hot-write anti-pattern, and the surfaces this limits are * already bounded cross-node — the client_credentials grant by the assertion * replay guard and its ≤60s `exp` window, CIMD fetches by the concurrency * caps. A node-local bucket is the right defense-in-depth without turning * every token request into a replicated write. * * Buckets refill continuously (elapsed-time based — no interval timers), so a * limit of N/min admits a burst of up to N and then one request per 60/N * seconds. Keys may be ATTACKER-CHOSEN strings (client_ids, URLs), so: (1) the * key space is LRU-bounded — at `maxKeys` the least-recently-used bucket is * evicted (an evicted bucket forgets that key's spend history, acceptable * because eviction requires `maxKeys` distinct keys inside one window and the * global concurrency caps still bound aggregate work); and (2) the key is * stored as a fixed-size SHA-256 fingerprint, so a flood of long distinct keys * bounds map memory to `maxKeys` × a constant, not × the raw key length. */ export type RateLimitResult = { allowed: true; } | { allowed: false; retryAfterSeconds: number; }; export type RateLimiter = { tryTake(key: string): RateLimitResult; /** Drop all bucket state (for testing). @internal */ _reset(): void; }; export declare function createRateLimiter(options: { /** Bucket capacity (maximum burst). */ capacity: number; /** Continuous refill rate, tokens per minute. */ refillPerMinute: number; /** LRU bound on distinct keys tracked. Default 10 000. */ maxKeys?: number; /** Clock override (for testing). Default Date.now. */ now?: () => number; }): RateLimiter;