makeIpRateLimiter(prefix: string,maxAttempts: number,lockoutMs: number,): { isLimited: (ip: string) => Promise<boolean>; record: (ip: string) => Promise<boolean>; }
Build a namespaced per-IP limiter: isLimited checks the lockout,
record counts one attempt (locking out at maxAttempts for lockoutMs
and returning true once locked). Each caller picks its own prefix so
counters never collide across features.