/** * Per-key brute-force protection limiter. * * Semantics: * - `fail(key)` records a failed attempt; reaching `maxFailures` within * `windowMs` triggers a `blockDurationMs` lockout. * - `succeed(key)` clears all state for the key (a successful auth wipes the * bucket so legitimate users aren't punished for prior typos). * - `check(key)` is read-only — returns the current block status. * - Failures from the same key within `burstCoalesceMs` collapse to a single * attempt. This absorbs SPA fan-out / SDK auto-retry storms without * weakening protection: a deliberate attacker is rate-limited to ~1 * attempt/s per key, still astronomically slow against any token of * meaningful entropy. * * Pure counter — no policy knowledge (loopback exemption, key formatting, * etc. all live in the policy layer that calls into this limiter). */ import { type Clock } from './keyed-store.js'; export type FailureLimiterConfig = { maxFailures: number; windowMs: number; blockDurationMs: number; /** @default 1000 */ burstCoalesceMs?: number; /** Retain idle keys for this long. @default `windowMs + blockDurationMs` */ staleAfterMs?: number; clock?: Clock; }; export type FailureCheck = { blocked: false; } | { blocked: true; retryAfterSec: number; }; export declare class FailureLimiter { private readonly store; private readonly clock; private readonly maxFailures; private readonly windowMs; private readonly blockDurationMs; private readonly burstCoalesceMs; constructor(cfg: FailureLimiterConfig); check(key: string): FailureCheck; fail(key: string): void; succeed(key: string): void; /** @internal Test hook. */ resetForTests(): void; destroy(): void; }