Skip to content

Rate limits

z4j applies token-bucket rate limits to public auth paths and to admin paths whose write fan-out can be expensive. Authenticated read paths are not capped.

All values are the in-code defaults defined in z4j_brain.domain.ip_rate_limit. The window is 60 seconds.

Endpoint Cap (per IP)
POST /api/v1/auth/login 20 / min
POST /api/v1/auth/password-reset/request and /confirm 10 / min (shared bucket)
GET /api/v1/invitations/preview and POST /api/v1/invitations/accept 30 / min (shared bucket)
Bulk-write admin actions (bulk-delete tasks, bulk-retry, purge-queue, schedule trigger / import / resync) 10 / min (shared bucket)
Notification-channel /channels/test preflight 20 / min
Agent WebSocket connects dedicated bucket on the /ws endpoint

Exceeding any limit returns 429 Too Many Requests.

In addition to the per-IP login bucket, the brain tracks failed logins per account:

Setting Default Meaning
Z4J_LOGIN_LOCKOUT_THRESHOLD 10 Failed attempts before lockout.
Z4J_LOGIN_LOCKOUT_DURATION_SECONDS 900 (15 min) How long the lockout lasts.
Z4J_LOGIN_BACKOFF_BASE_SECONDS 0.5 Per-failure incremental backoff applied to the response.
Z4J_LOGIN_BACKOFF_MAX_SECONDS 5.0 Cap on that backoff.
Z4J_LOGIN_MIN_DURATION_MS (settings) Minimum response time held server-side so timing is not exploitable.

The lockout is per-account, so an attacker rotating IPs to evade the per-IP cap still hits the account-level wall.

The brain does not emit X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset today. Detect throttling via the 429 status alone.

Tunables for login lockout and account backoff live in Settings (see above and the env-vars reference). The per-IP bucket sizes are in-code defaults; changing them requires a code change.

/health and /metrics are not rate-limited; scraping is expected.