Problem
Public API routes under src/pages/api/** have no rate limiting. The J0dI3 proxy endpoints (/api/j0di3/**) are particularly concerning because they proxy to a paid AI backend — an authenticated attacker (or a misbehaving client) could drive costs up quickly. The contact endpoint could be abused for spam, and /api/health could be pinged in a tight loop.
Expected behavior
Every public API route enforces a sensible per-IP and per-session rate limit, and returns 429 Too Many Requests with Retry-After when exceeded.
Acceptance criteria
Risk if not done
- J0dI3 cost overrun (paid AI calls).
- Contact form spam.
- Accidental DoS from a buggy client in a tight loop (happens more often than malicious attacks).
Suggested approach
// src/lib/rate-limit.ts
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
export const j0di3Limiter = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(20, "1 m"),
});
Then in each route:
const { success } = await j0di3Limiter.limit(`j0di3:${session.user.id}`);
if (!success) return res.status(429).json({ error: "Rate limit exceeded" });
Problem
Public API routes under
src/pages/api/**have no rate limiting. The J0dI3 proxy endpoints (/api/j0di3/**) are particularly concerning because they proxy to a paid AI backend — an authenticated attacker (or a misbehaving client) could drive costs up quickly. The contact endpoint could be abused for spam, and/api/healthcould be pinged in a tight loop.Expected behavior
Every public API route enforces a sensible per-IP and per-session rate limit, and returns
429 Too Many RequestswithRetry-Afterwhen exceeded.Acceptance criteria
@vercel/kv, since deployment target is Vercel).docs/so frontend knows what to expect.Risk if not done
Suggested approach
Then in each route: