Anonymous temporary room chat and file drop built with Vite, Cloudflare Workers, Durable Objects, and R2.
- 6-digit temporary rooms
- Real-time room presence and chat over WebSocket
- File upload and download through the Worker
- Image, audio, and video previews
- Room expiry and room extension
- Desktop and mobile room presence UI
- Dark, light, and system theme support
- Local slash commands:
/name <new name>/theme/help
- Vite
- TypeScript
- Hono
- Cloudflare Workers
- Durable Objects
- R2
src/views/*: page HTML renderingsrc/client/*: frontend behaviorsrc/app.css: app stylessrc/index.ts: Worker entrysrc/routes/*: HTTP and page routessrc/room/durable/*: room state Durable Objects
- Node.js 20+
- pnpm
- Wrangler 4
- A Cloudflare account with:
- one R2 bucket
- Durable Objects enabled
- a Rate Limiting binding
pnpm installpnpm devThe app runs on http://localhost:5173.
pnpm buildpnpm run deployThe deploy script reads .env.prod by default. Values in .env.prod override
[vars] in wrangler.toml.
You can also override the env file:
pnpm run deploy -- .env.stagingTo bypass the sync step and run Wrangler directly:
pnpm run deploy:rawMain config lives in wrangler.toml.
Current bindings and vars:
- Durable Objects:
ROOMS,ROOM_INDEX - Rate limit:
ROOM_JOIN_RATE_LIMIT - Default vars in
wrangler.toml:MAX_FILE_SIZE_MB,ROOM_TTL_HOURS,BLOCKED_MIME_TYPES - Required deploy vars from
.env.produnless you also hardcode them intowrangler.toml:ADMIN_AUTH_TOKEN,R2_ACCOUNT_ID,R2_BUCKET_NAME,R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY
You should update at least:
- rate limit namespace
ADMIN_AUTH_TOKENR2_ACCOUNT_IDR2_BUCKET_NAMER2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEY
Internal stats endpoint:
GET /api/v1/admin/stats
X-Admin-Token: <token>
Example:
curl -s http://127.0.0.1:5173/api/v1/admin/stats \
-H 'X-Admin-Token: your_token'- Create a room with
POST /api/v1/rooms - Open
/room/<roomKey> - Join via
POST /api/v1/rooms/:key/join - Chat and upload files
- Extend room lifetime with
POST /api/v1/rooms/:key/extend
- Files are stored in R2 under
rooms/<roomKey>/... - Uploads are direct-to-R2 via presigned PUT URLs generated by the Worker
- Downloads are proxied by the Worker through the R2 S3 API instead of exposing R2 directly
- Room metadata and presence are stored in Durable Objects
- Expired rooms are cleaned up by the scheduled cleanup job through the R2 S3 API
- Your R2 bucket must allow browser CORS for direct uploads. At minimum, allow:
- origin: your site origin
- methods:
PUT - headers:
content-type,content-disposition,x-amz-meta-originalfilename