Skip to main content

Overview

Authenticated rate limits now use a server-side reputation layer. The layer scores users, sessions, API keys, IPs, CIDRs, and user-location pairs from auditable signals, then returns a coarse risk tier and rate-limit multiplier to the API and PostgREST guards. The policy is intentionally server-only. The repository is open source, so do not move scoring thresholds, bypass rules, or detailed reason codes into client code, response headers, or public docs. User-agent, missing-header, and request-shape checks are weak signals; they can raise caution but cannot earn trust by themselves.

Trust Tiers

TierOperator meaningRate-limit behavior
trustedSustained low-risk, organic usage with no recent abuseHigher authenticated budgets
standardNormal authenticated activity or fail-safe defaultCurrent authenticated budgets
watchSuspicious but not high-confidence abuseStandard or slightly lower budgets
challenge_requiredMedium-risk browser mutation activityBrowser mutation routes require Turnstile step-up
restrictedHigh-risk activity or manual restrictionStricter budgets and normal abuse blocks still apply
Trust never bypasses active user suspension, active IP blocks, sensitive auth route protections, workspace-secret hard overrides, or severe backend abuse cascades.

API Abuse IP Blocks

Session-authenticated routes may defer proxy-side api_abuse IP blocks only long enough to validate route authentication. Do not treat bearer-shaped tokens, ttr_ strings, Supabase auth cookie names, or app-session cookie presence as proof of an authenticated session. If the route auth check fails, return the original block response before invoking the handler or recording normal auth failure side effects.

Signals

The layer records rolling signals for:
  • rate-limit hits, failed auth, repeated 4xx, 401, 403, and 429
  • payload abuse and automation-like clients
  • missing or scripted browser headers as weak negative signals
  • older accounts, stable sessions, successful organic usage, and passed challenges as positive signals
  • admin overrides and override revocations
If reputation lookup, challenge verification, or signal recording fails, request handling must fall back to standard limits. Failures must never grant elevated limits.

Backend 429 Cascades

Supabase Auth and PostgREST/backend 429 responses are availability signals, not user-scoped abuse proof. Handlers may return 429 to the caller and seed a bounded proxy/IP throttle when the client IP is known, but a single backend rate limit must not create or extend user_suspensions for the authenticated user. User suspension requires manual operator action or repeated, user-attributable abuse counters, and automated suspensions should be expiry-bound unless a separate policy explicitly justifies permanence.

Shared-IP Login Traffic

English centers, classrooms, and offices often put many legitimate teachers behind one public NAT IP. The proxy guard therefore keeps password login, OTP send, and OTP verify on separate auth buckets instead of sharing the generic mutation budget:
PolicyDefault minute/hour/day budgetOverride variables
password-login60 / 600 / 4000API_PROXY_PASSWORD_LOGIN_LIMIT_MINUTE, API_PROXY_PASSWORD_LOGIN_LIMIT_HOUR, API_PROXY_PASSWORD_LOGIN_LIMIT_DAY
otp-send30 / 180 / 300API_PROXY_OTP_SEND_LIMIT_MINUTE, API_PROXY_OTP_SEND_LIMIT_HOUR, API_PROXY_OTP_SEND_LIMIT_DAY
otp-verify60 / 600 / 4000API_PROXY_OTP_VERIFY_LIMIT_MINUTE, API_PROXY_OTP_VERIFY_LIMIT_HOUR, API_PROXY_OTP_VERIFY_LIMIT_DAY
OTP send also has a shared-IP abuse guard with ABUSE_OTP_SEND_IP_LIMIT_MINUTE, ABUSE_OTP_SEND_IP_LIMIT_HOUR, and ABUSE_OTP_SEND_IP_LIMIT_DAY. Keep the per-email cooldown/hour/day limits in place; they are the primary protection against repeated sends to the same mailbox. Human auth route 429 responses should return Retry-After to the caller and must not be converted into api_abuse IP blocks by the proxy escalation path. Generic anonymous API route-limit hits can still escalate.

Step-Up Challenges

Browser mutations with medium risk should require Turnstile through the existing server verification path. The API returns a generic challenge-required response without exposing the exact scoring rules. API keys, CLI calls, cron jobs, webhooks, and native clients do not receive browser challenges; they rely on token, workspace, and API-key reputation instead.

Local E2E Isolation

The Playwright rate-limit suite intentionally exhausts budgets and records 429 signals. Its resetDbRateLimits() helper must clear both PostgREST rate-limit counters and generated adaptive abuse state (abuse_activity_signals, abuse_step_up_challenges, and abuse_reputation_subjects) before each spec. The local dev-session endpoint also supports resetRateLimits: true to clear the app process’s in-memory rate-limit fallback during E2E setup. Specs that intentionally hit authenticated route limits should also use a fresh local dev-session account per test/retry so Redis-backed user keys and asynchronous adaptive reputation writes cannot bleed into the next case.

Admin Investigation

Use Infrastructure -> Abuse Intelligence to inspect:
  • trusted, watched, and restricted subject counts
  • recent signals and coarse reason codes
  • challenge pass/fail trends
  • risky users, sessions, API keys, IPs, and CIDRs
  • active manual overrides
When investigating a false positive:
  1. Check whether the subject has recent rate-limit, auth-failure, or payload abuse signals.
  2. Compare the subject with related location and user-location entries.
  3. Review challenge outcomes and recent route diversity before granting trust.
  4. Add a time-bound override only when the activity is clearly organic.
  5. Revoke trust immediately if the subject later shows scripted behavior, account takeover indicators, or noisy API-key traffic.
For a live classroom login incident, ask for the center’s current public IP and check Infrastructure -> Abuse Intelligence for active IP blocks or abuse events with api_abuse or password_login_failed. If the activity is confirmed organic, clear the active false-positive block or reset the affected counters; do not relax the per-email OTP cooldown or failed-attempt protections. Manual overrides require a reason and are written to the audit signal stream. Prefer expiry-bound overrides for trust and watch decisions so stale operational judgment does not become permanent policy.