Task graph covers all Phase 1 concerns: config system, TLS termination, proxy handler, operations (rate limiting, logging, health check, admin socket, signals, shutdown, body size limit), deployment artifacts, and two review checkpoints. No circular dependencies. Critical path length of 7. Risk distribution: 3 high-risk (ACME, TLS listener setup, startup orchestration), 7 medium, 11 low, 2 trivial.
3.4 KiB
3.4 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| ops/rate-limiting | Implement token bucket rate limiting with IPv6 /64 normalization and background eviction | pending |
|
moderate | medium | component | implementation |
Description
Implement per-IP token bucket rate limiting as axum middleware. This runs before the proxy handler and rejects requests that exceed the rate limit with 429 Too Many Requests.
Token Bucket Algorithm
- Nodelay semantics matching nginx's
limit_req burst nodelay - When bucket is empty, request is immediately rejected with 429 — no queuing
- Tokens added at rate of
requests_per_second(1 token every1000ms / requests_per_second) - Bucket capacity is
burstvalue - Per-IP in Phase 1 (not per-site)
IPv6 Normalization
- IPv4: Rate limited per individual address (
/32) - IPv6: Rate limited per
/64prefix. All addresses in the same/64share a token bucket - Normalize IPv6 addresses to their
/64prefix before bucket lookup
Rate Limit State
Arc<Mutex<HashMap<IpAddr, TokenBucket>>>shared via axum State- Token bucket struct with:
tokens: f64,last_refill: Instant,rate: f64,max: u32
Background Eviction Task
- Runs every 60 seconds (configurable)
- Removes entries whose last access timestamp is older than 300 seconds (5 minutes default)
- Prevents unbounded memory growth
Config Reload Behavior
When rate limit parameters change:
- New
DynamicConfigswapped in via ArcSwap - On next request from an existing IP, rate limiter reads current DynamicConfig
- Token bucket refills using new rate, capacity set to new burst
- If current token count exceeds new burst max, cap to new burst max
- HashMap is NOT cleared — avoids rate-limiting gap
Logging
Rate limit events logged with RATE_LIMIT prefix:
RATE_LIMIT client_ip=203.0.113.50 host=Y.Z path=/W status=429
Middleware Integration
Rate limiting runs as tower middleware before the proxy handler in the axum router.
Acceptance Criteria
- Token bucket implementation with nodelay semantics
- Per-IP rate limiting with configurable rate and burst
- IPv6 addresses normalized to
/64prefix before bucket lookup - IPv4 addresses used as-is (
/32) - Background eviction task removes stale entries every 60 seconds
- Config reload: new rate/burst parameters adopted on next request from existing IP
- Token count capped to new burst max when burst decreases
- HashMap not cleared on config reload (no rate-limiting gap)
429 Too Many Requestsresponse withToo Many RequestsbodyRATE_LIMITprefixed log event withclient_ip,host,path,status- Rate limiter state shared via
Arc<Mutex<HashMap<IpAddr, TokenBucket>>> - Unit tests for token bucket algorithm (fill, drain, reject)
- Unit tests for IPv6
/64normalization - Integration test: requests above rate limit receive 429
References
- docs/architecture/operations.md — rate limiting section
- docs/architecture/decisions/006-rate-limiting-approach.md — token bucket rationale
Notes
The rate limiter must be efficient on the hot path — no locks on reads. Consider using a
DashMapor similar concurrent map instead ofMutex<HashMap>for better read performance. The spec saysMutex<HashMap>but an implementation agent may choose a more performant concurrent data structure.
Summary
To be filled on completion