A conventional per-key rate limiter allocates one record per active key. Under a volumetric DDoS attack from millions of distinct source IPs, that per-key state is itself the memory-exhaustion vector — the attacker doesn’t need to exceed your rate limit, they just need to send enough unique keys to exhaust your process heap.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/AmeyaBorkar/throttlekit/llms.txt
Use this file to discover all available pages before exploring further.
sketchRateLimit solves this by replacing the per-key map with a single Count-Min Sketch (CMS) whose memory footprint depends only on the accuracy parameters, never on how many distinct keys are seen.
sketchRateLimit()
sketchRateLimit is @experimental — excluded from the 1.x SemVer guarantee; the interface may change in a minor release.
SketchRateLimitOptions
Maximum requests admitted per key within each window. A hard ceiling — never exceeded.
Window width in milliseconds. Windows are epoch-aligned:
floor(now / windowMs) * windowMs.Additive accuracy. The sketch overestimates a key’s count by at most
epsilon × N (where N is the total admitted mass in the window) with probability >= 1 - delta. Smaller is more accurate but uses more memory (width = ceil(e / epsilon)). Default 0.01.Failure probability for the
epsilon bound. Smaller means more reliable but uses more memory (depth = ceil(ln(1 / delta))). Default 0.001.Use the Estan–Varghese conservative-update rule to tighten the overestimate while preserving the never-underestimate guarantee. Default
true.32-bit hash seed. Defaults to a per-instance random value so an attacker cannot precompute keys that collide with a victim and grief it into false denial. Only override with a fixed seed in tests.
Injected time source. Defaults to the system clock.
SketchRateLimiter Interface
capacity is the total number of counters (depth × width). At default parameters (epsilon=0.01, delta=0.001), this is approximately 1,850 counters × 4 bytes = ~7.4 KB, regardless of how many keys are observed.
cost must be a positive integer. The counters are Uint32Array, so a fractional cost would truncate and break the never-over-admit guarantee. Passing a non-integer cost throws a RangeError.The Safety Guarantee
The guarantee is hard and non-probabilistic:BecauseThe limiter never over-admits. Its only error is in the safe direction: it may deny a key slightly early once hash collisions inflate its estimate. By the CMS bound that early-denial probability is bounded byestimate(key) >= trueCount(key)always, analloweddecision implies the true admitted count for that key is<= limit.
epsilon × N (the total admitted mass in the window) with probability >= 1 - delta.
Over-denying (never over-admitting) is exactly the right bias for DDoS and abuse protection: a false-positive denial for a legitimate key is tolerable; a false-negative that lets a flood through is not.
When to Use
| Scenario | Recommended limiter |
|---|---|
| High-value API keys, billing, auth | rateLimit (exact, per-key store) |
| Public API with trusted, bounded key space | rateLimit with l1.maxKeys |
| DDoS mitigation — millions of distinct source IPs | sketchRateLimit |
| Public endpoint with untrusted, unbounded key universe | sketchRateLimit or twoTier with l1.maxKeys |
| Cluster-wide heavy-hitter detection (best-effort) | mergeableSketch |
Full Example
mergeableSketch() — Distributed Use
For cluster-wide frequency estimation (e.g. detecting which source IPs are heavy hitters across a fleet), each node keeps its own mergeableSketch and periodically shares its state with peers. Because CMS counters are purely additive, summing node sketches yields the union — a global frequency estimate in the same fixed footprint.
MergeableSketchOptions
Additive accuracy. Default
0.01.Failure probability. Default
0.001.Hash seed. All merging nodes must use the same seed. Defaults to a fixed shared constant (
0x9e3779b1) so peers merge out of the box.sketchSnapshotFromBytes(bytes)
Decodes bytes produced by MergeableSketch.toBytes() back into a SketchSnapshot for merging. Validates that total is finite and non-negative; a poisoned peer total would corrupt the cluster-wide N in the epsilon × N error bound and is rejected.
FAQ: Why is epsilon × N the error term, not just epsilon?
FAQ: Why is epsilon × N the error term, not just epsilon?
The Count-Min Sketch bound is an additive approximation: the overcount is at most
epsilon × N where N is the total number of items added to the sketch across all keys in the current window. If one window sees 10,000 total requests and epsilon = 0.01, the worst-case overcount for any single key is 100. This means with heavy total traffic, a key that actually had 0 requests might appear to have up to 100 — causing early denial. Shrinking epsilon tightens this, at the cost of more memory.FAQ: How does the per-instance random seed protect against hash-collision attacks?
FAQ: How does the per-instance random seed protect against hash-collision attacks?
An attacker who knows the sketch’s hash seed can precompute a set of keys that all hash to the same counter positions. If those counter positions belong to a target victim key, the attacker can inflate the victim’s estimate and cause it to be falsely denied (a griefing attack). A random per-instance seed makes this precomputation impossible: the attacker cannot know the seed without observing the running process. Only pass a fixed
seed in tests or when you are certain the endpoint is not publicly reachable.