Skip to content

Rate limits (security)

Every unauthenticated path is a potential attack surface. Rate limits are the first line of defense against credential stuffing, enumeration, and amplification.

See operations § rate limits for the operational reference; this page is the “why.”

  • IP limit (5/min) - protects against burst attacks.
  • Email limit (10/min) - protects a single account when the attacker rotates IPs.
  • Combined with argon2id (50–80ms per attempt), this caps realistic throughput.
  • IP limit (5/min) - prevents spam.
  • Email limit (3/hour) - prevents user-harassment by flooding inbox.
  • Always returns “check your email” regardless of whether email exists (enumeration defense).
  • IP limit (10/min) - prevents token brute force.
  • Tokens are 128 bits of entropy → brute-force infeasible anyway, but belt-and-braces.
  • IP limit (3/hour) - one setup per boot. Short window, aggressive limit.
  • The setup token itself is single-use; this guards against token guessing.

Bulk operations (/tasks/bulk-retry, /queues/*/purge)

Section titled “Bulk operations (/tasks/bulk-retry, /queues/*/purge)”
  • User limit (10/min, 10/hour respectively) - protects Postgres from runaway sweeps.
  • Single-replica: in-process token buckets (fast, no external dep).
  • Multi-replica (v1.1+): Postgres-backed buckets (shared across replicas).

None. There is no “trusted IP” list in v1.0 - even localhost is rate-limited at the same rates. Sign in and use an authenticated endpoint if you need higher rates for programmatic work.

On 429, always included. Respect it in clients - retrying sooner extends the penalty.

z4j_rate_limit_denied_total{endpoint} counter. Alert on unusual spikes as an attack signal.