DocsServer features
Rate limiting
Protect each Rahti process with a bounded sliding-window budget before expensive application work begins.
Default policy
Rahti admits 200 dynamic requests per client IP per minute and retains at most 10,000 client buckets. Pages, APIs, RPCs, redirects, missing routes, and WebSocket handshakes share the budget.
OPTIONSrequests are exempt./health,/ready, and development tooling are exempt.- A GET or HEAD is exempt only when it resolves to a real file beneath the configured public directory.
Response contract
| Header | Meaning |
|---|---|
| RateLimit-Limit | Requests in the active policy. |
| RateLimit-Remaining | Requests left in this client's window. |
| RateLimit-Reset | Seconds until the oldest counted request expires. |
| Retry-After | Present on HTTP 429 with the reset duration. |
RPC refusals retain the JSON error shape; other requests receive a small HTML response. Security headers still apply because the limiter sits inside them.
Configure during startup
use std::time::Duration;
use rahti::rate_limit::{self, RateLimit};
rate_limit::configure(RateLimit {
requests: 600,
window: Duration::from_secs(60),
max_clients: 25_000,
trust_forwarded_headers: false,
exempt_paths: vec!["/health".into(), "/ready".into(), "/metrics".into()],
});RateLimit::per_second, per_minute, and per_hour are concise constructors; disabled() keeps the generated layer transparent. Reconfiguring replaces the policy and clears process-local buckets.
Client IP and proxy trust
Deployment boundary
Buckets disappear on restart and are independent in every replica. Treat the built-in limiter as an application-instance safety net; enforce durable, fleet-wide, account-based, or DDoS-grade limits at a gateway or shared rate-limit service.
