Rate Limiting
Fixed-window rate limiting backed by Cloudflare KV, protecting login, MFA, and payment webhook endpoints.
A fixed-window rate limiter backed by Cloudflare KV, implemented twice — once per Worker runtime, same shape and semantics in both:
apps/app/server/utils/rate-limit.ts— useshubKV()inside the App Worker's Nitro runtime.apps/functions/src/rate-limit.ts— takes a rawKVNamespace(AuthKV) directly, since the auth Worker has no Nitro/NuxtHub to callhubKV()from.
export async function rateLimit(key: string, { limit, windowSeconds }) {
// Cloudflare KV rejects a TTL under 60s, so the window can't be shorter.
const window = Math.max(windowSeconds, 60);
const kv = hubKV();
const windowId = Math.floor(Date.now() / 1000 / window);
const kvKey = `ratelimit:${key}:${windowId}`;
const count = ((await kv.get<number>(kvKey)) ?? 0) + 1;
await kv.set(kvKey, count, { ttl: window });
return { allowed: count <= limit, count, limit };
}
assertNotRateLimited(event, routeKey, options) wraps this for HTTP routes on the App Worker: it keys by <routeKey>:<ip> and throws 429 Too Many Requests directly when the limit is exceeded.
What's currently protected
| Where | Keyed by | Purpose |
|---|---|---|
apps/functions/src/auth.ts (enforceCredentialRateLimit) | path + IP + email | Password/code login on the auth Worker |
layers/admin/server/api/admin/mfa/verify.post.ts | platform admin id | MFA enrollment attempts |
layers/admin/server/api/admin/mfa/challenge.post.ts | platform admin id | MFA reauthentication attempts |
layers/billing/server/api/webhooks/stripe.post.ts | IP | Stripe webhook, on top of signature validation |
layers/billing/server/api/webhooks/paddle.post.ts | IP | Paddle webhook, on top of signature validation |
This isn't a general-purpose API throttle — it's applied specifically where credential/signature validation alone doesn't stop a high-volume replay: authentication attempts and unauthenticated webhook endpoints. There's no rate limiting on ordinary tRPC procedures today.
Ready to build and launch your SaaS?
Get 100% full source code ownership, zero proprietary wrappers, and architecture engineered for millions of requests on Cloudflare.