From bb7632fd4d8c18ef2f0a38b1df65d22a414991ad Mon Sep 17 00:00:00 2001 From: fen Date: Thu, 10 Sep 2026 11:13:34 -0500 Subject: [PATCH] Drop duplicate rate-limits page, link API.md to Security Notes rate-limiting section (#144) --- API.md | 2 +- rate-limits.md | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 rate-limits.md diff --git a/API.md b/API.md index 1897e44..02ab1d3 100644 --- a/API.md +++ b/API.md @@ -43,7 +43,7 @@ machine-readable `code` the web UI maps to plain-language guidance (#105): | `slug_reserved` | 400 | custom slug is reserved | | `expiry_invalid` | 400 | expires_in out of 1 minute – 1 year range | | `content_too_large` | 413 | content or body exceeds the size cap | -| `rate_limited` | 429 | too many requests; see `Retry-After` | +| `rate_limited` | 429 | too many requests; see `Retry-After`; see [Security Notes](security.md) for limiter details | Other statuses: `400` invalid body, `401` password required, `404` paste expired/burned/gone. Unknown codes should be treated as a generic failure. diff --git a/rate-limits.md b/rate-limits.md deleted file mode 100644 index 8af3171..0000000 --- a/rate-limits.md +++ /dev/null @@ -1,18 +0,0 @@ -# Rate Limits - -Palette rate limits paste creation per client IP: burst of 5, then 1 request per second. Exceeding it returns `429` with a `Retry-After` header (see [API](API.md)). - -## Client IP resolution - -`clientIP` takes the **rightmost** entry in `X-Forwarded-For`, i.e. the address added by the trusted reverse proxy in front of Palette. This means spoofed forwarding headers cannot bypass or poison the rate-limit bucket. - -Verified against palette-dev (2026-09-09, issue #144): - -1. Six rapid `POST /api/pastes` from one IP: `201` x5, then `429`. -2. Repeated while sending spoofed headers `X-Forwarded-For: 1.2.3.4`, `X-Real-Ip: 9.9.9.9`, `True-Client-Ip: 3.3.3.3`: all requests still received `429`. No bypass, and the spoofed values did not shift the bucket. - -## Known tradeoff: shared egress IPs - -Because the bucket is keyed solely by client IP, everyone behind one egress IP (office network, VPN, carrier NAT) shares a single burst-5 / 1rps create budget. One heavy user can rate-limit everyone behind that IP. - -This is an inherent property of IP-keyed limiting without accounts, accepted for now. If it becomes a real problem, candidate mitigations include raising the create budget or adding per-session (cookie) buckets on top of the IP bucket.