Skip to content

sliding-window rate limiter scoped per tenant/API key #551

Description

@mikewheeleer

Summary

The current fixed-window limiter allows bursts at window edges and isn't isolated per tenant. Implement a sliding-window limiter keyed per tenant/API key so limits are smooth and fair.

Why this matters

Fixed windows let a client send 2x the limit across a boundary, and a shared counter lets one tenant starve others. A per-key sliding window is fair and burst-resistant.

Requirements

  • Implement a sliding-window counter keyed by tenant/API key.
  • Return 429 with Retry-After and a structured rate_limited error when exceeded.
  • Expose remaining/limit/reset via response headers.
  • Window size and limit are configurable with safe defaults.

Technical guidance

  • Keep the counter store behind an interface (in-memory impl now).
  • Compute the sliding count from the prior + current sub-windows to avoid storing every timestamp.

Edge cases — each must have a test

  • requests under the limit -> allowed with correct remaining header
  • burst across a window boundary -> still bounded (no 2x)
  • two tenants -> independent budgets
  • limit exceeded -> 429 with Retry-After
  • counter resets after the window

Acceptance criteria

  • All requirements and every edge case above implemented and covered by tests
  • New unit and integration tests; existing tests still pass and no regressions
  • Structured, typed errors (no leaked internals; stable codes)
  • npm run lint, npm test, and npm run build all pass locally
  • Code follows the repo's existing conventions; no duplication or dead code
  • Short docs/comments explaining the design and any non-obvious decisions
  • PR description explains the approach and includes Closes #<issue>

Out of scope

  • Distributed rate limiting
  • Dynamic per-plan limits

Rewards

Part of the GrantFox OSS / Official Campaign (FWC26) — this task may be rewarded. PR quality is assessed by AI: depth, correctness under edge cases, meaningful tests, and clean design are what earn the reward. Shallow changes (typos, formatting, trivial docs) do not qualify.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions