What
Replace the in-memory sliding-window rate limiter in app/src/lib/ratelimit.ts with a Vercel KV / Upstash Redis–backed implementation.
Why
The v0.5 limiter lives in a Map inside a single warm Vercel Function instance. A determined attacker that triggers cold starts (or hits multiple Vercel regions) bypasses the bucket. The 2026-05-11 review pass documented this in docs/honest-limits.md.
Acceptance
- Rate-limit decisions survive cold starts.
- Same 5 req/min/IP cap as v0.5.
x-forwarded-for trust still gated on process.env.VERCEL === "1".
- The 429 response shape stays compatible with the existing client expectations (
Retry-After header, X-RateLimit-*).
Refs
- v0.5 implementation:
app/src/lib/ratelimit.ts
- Wire-up:
app/src/app/api/consume/route.ts
- Deferral note:
docs/honest-limits.md
What
Replace the in-memory sliding-window rate limiter in
app/src/lib/ratelimit.tswith a Vercel KV / Upstash Redis–backed implementation.Why
The v0.5 limiter lives in a
Mapinside a single warm Vercel Function instance. A determined attacker that triggers cold starts (or hits multiple Vercel regions) bypasses the bucket. The 2026-05-11 review pass documented this indocs/honest-limits.md.Acceptance
x-forwarded-fortrust still gated onprocess.env.VERCEL === "1".Retry-Afterheader,X-RateLimit-*).Refs
app/src/lib/ratelimit.tsapp/src/app/api/consume/route.tsdocs/honest-limits.md