fix(guard): disable admission limits by default - #5689
Conversation
NathanFlurry
commented
Sep 10, 2026
- Disable per-IP request-rate and in-flight admission limits by default while preserving opt-in controls.
- Expose Guard retry, timeout, connection-pool, and admission-state settings through the configuration schema.
- Report request-rate and maximum-in-flight rejections as distinct errors with configured thresholds.
- Add focused coverage and document the self-hosted admission defaults.
|
🚅 Deployed to the actors-pr-5689 environment in rivet-frontend
|
Review: fix(guard): disable admission limits by defaultMakes Guard's per-IP rate limiting and max-in-flight admission control opt-in (previously hardcoded to always-on 10k req/min and 2k concurrent), and exposes a batch of previously-hardcoded proxy timeout/retry/pool-idle settings through guard.* config. Solid refactor overall: ClientState/InFlightPermit cleanly move to Option-based fields so the disabled path has no cache/lock overhead, Guard::validate() follows the existing Root::validate_and_set_defaults() pattern, and the new unit tests (guard.rs, proxy_service.rs, utils/tests.rs) directly cover the new default-off, independent-rejection, and zero-value-validation behavior. Bug: sub-second upstream_request_timeout_ms renders as "0 seconds" in the timeout errorupstream_request_timeout_ms is now configurable down to 1ms (schemars range min = 1 in engine/packages/config/src/config/guard.rs), but errors::RequestTimeout still reports timeout_seconds via timeout_duration.as_secs() (proxy_service.rs, in the handle_http_request timeout branch), and its message template is "Request timed out during PHASE after N seconds." An operator who sets guard.upstream_request_timeout_ms to 500 will see "0 seconds" reported on every timeout, which is misleading. Before this PR the timeout was hardcoded to a whole 30s, so as_secs() was always accurate; now that it's ms-configurable, the error should report milliseconds instead (or at least not silently truncate to 0). Worth confirming: this flips the default DoS posture at the edgePreviously every Guard instance enforced a built-in rate limit (10k req/min) and in-flight cap (2k) per source IP with no way to disable them. After this change both are off unless an operator explicitly sets guard.rate_limit / guard.max_in_flight. Per this repo's trust-boundary conventions, the client-to-engine path is untrusted and Guard sits on that boundary, so self-hosted deployments that don't proactively configure these now have no built-in protection against a single IP flooding requests or holding many concurrent connections. That may well be the right call if the old fixed defaults were causing false-positive throttling in practice (the "fix" framing suggests this was the motivation), but it's worth making sure this default flip is called out clearly for upgraders (release notes / changelog) beyond the limits.mdx table update, since it's a meaningful change in out-of-the-box security posture. Minor / FYI
Nice targeted test coverage for the new behavior (admission_limits_are_disabled_by_default, configured_admission_limits_are_enforced_independently, client_state_reports_rate_limit_separately, etc.) using the Option-based ClientState refactor to verify the hot path stays a no-op when both controls are disabled. 🤖 Generated with Claude Code |
Engine previewOfficial multi-architecture debug images built from PR head docker pull rivetdev/engine:full-1625f46
docker pull rivetdev/engine:slim-1625f46Immutable multi-architecture manifests:
Publish workflow: https://github.com/rivet-dev/actors/actions/runs/34423140884 |
| if guard_config.rate_limit().is_some() || guard_config.max_in_flight().is_some() { | ||
| Some( | ||
| Cache::builder() | ||
| .max_capacity(guard_config.admission_client_state_cache_capacity()) | ||
| .time_to_idle(guard_config.admission_client_state_cache_idle_timeout()) |
There was a problem hiding this comment.
🟠 Medium · Keep admission state alive while requests hold permits
This cache can evict an IP while its ClientState is still held by an InFlightPermit. With admission_client_state_cache_capacity: 1, admitting a request from a second IP can evict the first IP; a subsequent request from that first IP creates a new counter and is admitted even when max_in_flight: 1 is already occupied. The same state split resets an active fixed-window rate limit. time_to_idle has the same failure mode for a long-running request after the configured idle period.
Retain live admission entries independently of the bounded/idle cache, or make eviction conditional on there being no in-flight permits. The capacity policy must not create a second ClientState for an IP with an active permit.