the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add Redis-backed rate limiter middleware

Enable createRateLimiter globally with a 60s window and 300 max per IP

Use Redis sorted sets to track request timestamps, set standard
rate-limit headers, and fail open if Redis is unavailable.

+61
+2
apps/api/src/index.ts
··· 9 9 import API from "./xrpc"; 10 10 import ssh from "./ssh"; 11 11 import tty from "./tty"; 12 + import { createRateLimiter } from "./ratelimiter"; 12 13 13 14 let server = createServer({ 14 15 validateResponse: false, ··· 26 27 app.use(contextMiddleware); 27 28 app.use(cors()); 28 29 app.use(morgan("dev")); 30 + app.use(createRateLimiter({ windowMs: 60_000, max: 300 })); 29 31 30 32 const banner = ` 31 33 ___ __ __
+59
apps/api/src/ratelimiter.ts
··· 1 + import type { RequestHandler } from "express"; 2 + 3 + interface RateLimiterOptions { 4 + windowMs?: number; 5 + max?: number; 6 + keyPrefix?: string; 7 + } 8 + 9 + export function createRateLimiter( 10 + options: RateLimiterOptions = {}, 11 + ): RequestHandler { 12 + const windowMs = options.windowMs ?? 60_000; // 1 minute 13 + const max = options.max ?? 100; 14 + const keyPrefix = options.keyPrefix ?? "rl"; 15 + 16 + return async (req, res, next) => { 17 + const ip = 18 + (req.headers["x-forwarded-for"] as string | undefined) 19 + ?.split(",")[0] 20 + ?.trim() ?? 21 + req.socket.remoteAddress ?? 22 + "unknown"; 23 + 24 + const key = `${keyPrefix}:${ip}`; 25 + const now = Date.now(); 26 + const windowStart = now - windowMs; 27 + 28 + try { 29 + const redis = req.ctx.redis; 30 + 31 + const [, , count] = (await redis 32 + .multi() 33 + .zRemRangeByScore(key, 0, windowStart) 34 + .zAdd(key, { score: now, value: `${now}:${Math.random()}` }) 35 + .zCard(key) 36 + .pExpire(key, windowMs) 37 + .exec()) as [unknown, unknown, number, unknown]; 38 + 39 + const remaining = Math.max(0, max - count); 40 + 41 + res.setHeader("X-RateLimit-Limit", max); 42 + res.setHeader("X-RateLimit-Remaining", remaining); 43 + res.setHeader("X-RateLimit-Reset", Math.ceil((now + windowMs) / 1000)); 44 + 45 + if (count > max) { 46 + res.setHeader("Retry-After", Math.ceil(windowMs / 1000)); 47 + res 48 + .status(429) 49 + .json({ error: "Too many requests, please try again later." }); 50 + return; 51 + } 52 + 53 + next(); 54 + } catch { 55 + // Fail open — don't block traffic if Redis is unavailable 56 + next(); 57 + } 58 + }; 59 + }