Skip to content

feat: rate limiting parity — sliding window, keys, THROTTLE (#10)#28

Merged
cport1 merged 1 commit into
mainfrom
feat/rate-limit-parity
Jul 19, 2026
Merged

feat: rate limiting parity — sliding window, keys, THROTTLE (#10)#28
cport1 merged 1 commit into
mainfrom
feat/rate-limit-parity

Conversation

@cport1

@cport1 cport1 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Closes the last rate-limiting parity gap with @webdecoy/node: rate limiting is now a rule on the engine producing a proper THROTTLE, not a side-channel that merely nudged the bot score.

What changed

  • WebDecoy_Rate_Limit_Rule implements the SDK RuleInterface, so it evaluates in order alongside tripwires and filters — added last, so a deterministic tripwire/filter DENY wins over a THROTTLE for the same request.
  • Proper 429 — over-limit requests get 429 + Retry-After + X-RateLimit-Limit/Remaining/Reset. Previously an exceeded limit just added +25 to the score.
  • Sliding window — the classic two-bucket weighted approximation (prev·overlap + curr) in a persistent object cache (Redis/Memcached) when one is present, transparently falling back to the fixed-window DB counter otherwise. Most serious WP hosts run a persistent cache, giving shared cross-process state node's in-memory limiter can't keep across restarts.
  • keyBy — IP (default), IP+route, or logged-in user id (a WP-specific option; anonymous requests fall back to IP). Composite keys are hashed to fit the DB column.
  • Per-rule config in Protection settings (limit, window, algorithm, key, dry-run). The existing global limit becomes the default rule, so effective limits are unchanged for existing installs. Increment-then-check matches node's semantics.

Cleanup

Removed the standalone pre-engine rate-limit block and the now-dead handle_rate_limit_exceeded(). Also fixed a latent constructor bug where an absent config key re-read the undefined key instead of its default.

Tests

Stubbed $wpdb + object-cache harness: fixed and sliding admit/deny at the boundary, per-IP and per-user independence, dry-run returns ALLOW while recording, header metadata on throttle, and the sliding→fixed fallback when no persistent cache is present (16 checks). SDK suite still green (41).

Closes #10. Part of #16.

Refactors rate limiting into a rule on the engine, closing the parity gap
with @webdecoy/node's RateLimitRule.

- WebDecoy_Rate_Limit_Rule implements the SDK RuleInterface, so it evaluates
  in order with tripwires/filters (added last, so a deterministic DENY wins
  over a THROTTLE). Over-limit now returns a proper 429 + Retry-After +
  X-RateLimit-Limit/Remaining/Reset headers — previously an exceeded limit
  only added +25 to the bot score.
- Sliding window: the classic two-bucket weighted approximation in a
  persistent object cache (Redis/Memcached) when present, transparently
  falling back to the fixed-window DB counter otherwise.
- keyBy: IP (default), IP+route, or logged-in user id (WP-specific; anon
  falls back to IP). Composite keys are hashed to fit the DB column.
- Per-rule limit/window/algorithm/key/dry-run in Protection settings; the
  existing global limit becomes the default rule, so effective limits are
  unchanged for existing installs. Increment-then-check matches node.
- Removed the standalone pre-engine rate-limit block and the now-dead
  handle_rate_limit_exceeded().
- Fixed a latent constructor bug (absent config key re-read the undefined
  key instead of the default).

Verified fixed + sliding admit/deny, per-IP/per-user independence, dry-run,
header metadata, and sliding->fixed fallback with a stubbed harness.

Closes #10. Part of #16.
Co-authored-by: Claude <noreply@anthropic.com>
@cport1
cport1 merged commit ce5a350 into main Jul 19, 2026
3 checks passed
@cport1
cport1 deleted the feat/rate-limit-parity branch July 19, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rate limiting parity — sliding window, custom keys, THROTTLE semantics

1 participant