Skip to content

Rate limits

z4j applies in-process sliding-window limits to public auth paths, agent ingress, expensive write paths, OpenAPI generation, and the activity feed. Most authenticated read paths are uncapped, but authenticated status alone does not bypass a limiter.

Except where noted, these are the in-code defaults from z4j_brain.domain.ip_rate_limit and use a 60-second window. Each worker process has independent counters.

Endpoint Cap (per IP)
POST /api/v1/auth/login 20 / min
POST /api/v1/auth/password-reset/request and /confirm 10 / min (shared bucket)
POST /api/v1/invitations/preview and POST /api/v1/invitations/accept 30 / min (shared bucket)
Bulk writes (task bulk-delete, command bulk-retry and purge, durable bulk-retry creation, schedule trigger and resync, project channel and default-subscription creation) 10 / min (shared bucket)
Notification-channel test endpoints 20 / min (shared project/user bucket)
Notification-channel import endpoints 30 / min (shared project/user bucket)
Agent WebSocket connect and agent long-poll/event HTTP endpoints 600 / min (shared bucket)
OpenAPI JSON and docs endpoints 10 / min (shared bucket, even when authenticated)
MFA verification endpoints 10 / min by default; configured by Z4J_MFA_VERIFICATION_RATE_PER_MIN

POST /api/v1/setup/complete uses a separate 5 requests per 15 minutes per-IP bucket, followed by a database-backed attempt budget documented in rate limits (security). The authenticated activity feed has its own 60 requests per minute per-user, per-worker counter.

HTTP limiters return 429 Too Many Requests. An over-limit agent WebSocket upgrade is closed with code 4429 instead.

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 Deprecated compatibility value; the login path does not read it.
Z4J_LOGIN_BACKOFF_MAX_SECONDS 5.0 Deprecated compatibility value; the login path does not read it.
Z4J_LOGIN_MIN_DURATION_MS 300 Minimum response floor. It reduces timing differences but is not a whole-request constant-time guarantee.

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. Detect throttling via the 429 status alone.

Tunables for login lockout, MFA verification, and the durable setup budget live in Settings (see above and the env-vars reference). The other HTTP per-IP bucket sizes are in-code defaults; changing them requires a code change.

/health and /metrics are not rate-limited; scraping is expected. /metrics still applies its configured authentication policy.