Skip to content

fix(api): P1 — enforce per-tier API limits (Phase 1: per-account burst + usage meter + safety ceiling) #3199

Description

@koala73

Problem

The REST API advertises per-tier allowances ("API Starter — 1,000 requests/day") but enforces only a global per-IP window (600/60s) plus a per-key MCP limiter; the apiRateLimit entitlement field is declared and never read. Result: a truth-in-marketing gap, Starter/Business identical at the network layer, and Enterprise's advertised 1,000/60s unreachable behind the 600/60s per-IP cap.

Resolved design — Phase 1

Full plan + code grounding: docs/plans/2026-06-30-001-feat-per-tier-api-rate-limits-plan.md.

Two distinct limit types at the single gateway chokepoint (server/gateway.ts:1034):

  • Per-minute burst (infra/abuse) → hard 429, value-sourced from apiRateLimit (Starter 60 / Business 300 / Enterprise 1,000 per 60s).
  • Daily usage meter (commercial allowance) → counts every served request, never rejects at the allowance (new apiDailyAllowance: 1,000 / 10,000 / unlimited).
  • Safety ceiling at 10× the allowance → hard 429 (runaway/cost protection; Enterprise none).

Eligibility keys on isUserApiKey / isEnterpriseAuth (user wm_ keys carry no keyCheck.kind); eligible keys bypass the per-IP cap in enforce mode only (shadow keeps it active). Fail-open throughout. Ships shadow-first behind API_RATE_LIMIT_ENFORCE. Marketing + docs/usage-rate-limits.mdx reframed to "included allowance," single-sourced from the catalog.

Overage billing is Phase 2#4560.

Acceptance criteria (revised — the daily axis meters, it does not hard-cap)

  • Starter key, 70 req in 60s, enforce → 429 on Add INFRA variant: Gulf FDI critical infrastructure tracker #61 with X-RateLimit-* + Retry-After.
  • Starter key past 1,000 req/day, enforce → 200 (metered); 429 only at the 10,000 ceiling, Retry-After to next UTC midnight.
  • Business tolerates 300/60s before burst 429; ceiling 100,000/day.
  • Enterprise tolerates 1,000/60s; no daily ceiling.
  • Shadow mode keeps the per-IP backstop active (protection never drops below today).
  • Authenticated keys eligible via isUserApiKey / isEnterpriseAuth (not keyCheck.kind); anonymous / wms_ / downgraded keys keep the per-IP path.
  • 429 responses carry X-RateLimit-Limit/Remaining/Reset + Retry-After.
  • /pro marketing + docs/usage-rate-limits.mdx reference the same catalog numbers.

Open decisions before the enforce flip

10× ceiling justification, telemetry-durability audit query, "included" copy timing, 5xx-in-meter, per-account identity vs orgs — see plan OQ1–OQ5. (Phase 1 ships in shadow regardless; these gate the enforce flip, not the merge.)

Metadata

Metadata

Labels

High ValueMeaningful contribution to the projectP1High priority, fix soonarea: APIBackend API, sidecar, keysenhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions