Guidelines for AI agents working on this codebase.
This is a Cloudflare Worker that runs Moltbot in a Cloudflare Sandbox container. It provides:
- Proxying to the Moltbot gateway (web UI + WebSocket)
- Admin UI at
/_admin/for device management - API endpoints at
/api/*for device pairing - Debug endpoints at
/debug/*for troubleshooting
Note: The CLI tool is still named clawdbot (upstream hasn't renamed yet), so CLI commands and internal config paths still use that name.
src/
├── index.ts # Main Hono app, route mounting
├── types.ts # TypeScript type definitions
├── config.ts # Constants (ports, timeouts, paths)
├── auth/ # Cloudflare Access authentication
│ ├── jwt.ts # JWT verification
│ ├── jwks.ts # JWKS fetching and caching
│ └── middleware.ts # Hono middleware for auth
├── gateway/ # Moltbot gateway management
│ ├── process.ts # Process lifecycle (find, start)
│ ├── env.ts # Environment variable building
│ ├── r2.ts # R2 bucket mounting
│ ├── sync.ts # R2 backup sync logic
│ └── utils.ts # Shared utilities (waitForProcess)
├── routes/ # API route handlers
│ ├── api.ts # /api/* endpoints (devices, gateway)
│ ├── admin.ts # /_admin/* static file serving
│ └── debug.ts # /debug/* endpoints
└── client/ # React admin UI (Vite)
├── App.tsx
├── api.ts # API client
└── pages/
DEV_MODE- Skips CF Access auth AND bypasses device pairing (maps toCLAWDBOT_DEV_MODEfor container)DEBUG_ROUTES- Enables/debug/*routes (disabled by default)- See
src/types.tsfor fullMoltbotEnvinterface
When calling the moltbot CLI from the worker, always include --url ws://localhost:18789.
Note: The CLI is still named clawdbot until upstream renames it:
sandbox.startProcess('clawdbot devices list --json --url ws://localhost:18789')CLI commands take 10-15 seconds due to WebSocket connection overhead. Use waitForProcess() helper in src/routes/api.ts.
The CLI outputs "Approved" (capital A). Use case-insensitive checks:
stdout.toLowerCase().includes('approved')npm test # Run tests (vitest)
npm run test:watch # Run tests in watch mode
npm run build # Build worker + client
npm run deploy # Build and deploy to Cloudflare
npm run dev # Vite dev server
npm run start # wrangler dev (local worker)
npm run typecheck # TypeScript checkTests use Vitest. Test files are colocated with source files (*.test.ts).
Current test coverage:
auth/jwt.test.ts- JWT decoding and validationauth/jwks.test.ts- JWKS fetching and cachingauth/middleware.test.ts- Auth middleware behaviorgateway/env.test.ts- Environment variable buildinggateway/process.test.ts- Process finding logicgateway/r2.test.ts- R2 mounting logic
When adding new functionality, add corresponding tests.
- Use TypeScript strict mode
- Prefer explicit types over inference for function signatures
- Keep route handlers thin - extract logic to separate modules
- Use Hono's context methods (
c.json(),c.html()) for responses
README.md- User-facing documentation (setup, configuration, usage)AGENTS.md- This file, for AI agents
Development documentation goes in AGENTS.md, not README.md.
Browser
│
▼
┌─────────────────────────────────────┐
│ Cloudflare Worker (index.ts) │
│ - Starts Moltbot in sandbox │
│ - Proxies HTTP/WebSocket requests │
│ - Passes secrets as env vars │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Cloudflare Sandbox Container │
│ ┌───────────────────────────────┐ │
│ │ Moltbot Gateway │ │
│ │ - Control UI on port 18789 │ │
│ │ - WebSocket RPC protocol │ │
│ │ - Agent runtime │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
| File | Purpose |
|---|---|
src/index.ts |
Worker that manages sandbox lifecycle and proxies requests |
Dockerfile |
Container image based on cloudflare/sandbox with Node 22 + Moltbot |
start-moltbot.sh |
Startup script that configures moltbot from env vars and launches gateway |
moltbot.json.template |
Default Moltbot configuration template |
wrangler.jsonc |
Cloudflare Worker + Container configuration |
npm install
cp .dev.vars.example .dev.vars
# Edit .dev.vars with your ANTHROPIC_API_KEY
npm run startFor local development, create .dev.vars:
ANTHROPIC_API_KEY=sk-ant-...
DEV_MODE=true # Skips CF Access auth + device pairing
DEBUG_ROUTES=true # Enables /debug/* routesLocal development with wrangler dev has issues proxying WebSocket connections through the sandbox. HTTP requests work but WebSocket connections may fail. Deploy to Cloudflare for full functionality.
The Dockerfile includes a cache bust comment. When changing moltbot.json.template or start-moltbot.sh, bump the version:
# Build cache bust: 2026-01-26-v10Moltbot configuration is built at container startup:
moltbot.json.templateis copied to~/.clawdbot/clawdbot.json(internal path unchanged)start-moltbot.shupdates the config with values from environment variables- Gateway starts with
--allow-unconfiguredflag (skips onboarding wizard)
These are the env vars passed TO the container (internal names):
| Variable | Config Path | Notes |
|---|---|---|
ANTHROPIC_API_KEY |
(env var) | Moltbot reads directly from env |
CLAWDBOT_GATEWAY_TOKEN |
--token flag |
Mapped from MOLTBOT_GATEWAY_TOKEN |
CLAWDBOT_DEV_MODE |
controlUi.allowInsecureAuth |
Mapped from DEV_MODE |
TELEGRAM_BOT_TOKEN |
channels.telegram.botToken |
|
DISCORD_BOT_TOKEN |
channels.discord.token |
|
SLACK_BOT_TOKEN |
channels.slack.botToken |
|
SLACK_APP_TOKEN |
channels.slack.appToken |
Moltbot has strict config validation. Common gotchas:
agents.defaults.modelmust be{ "primary": "model/name" }not a stringgateway.modemust be"local"for headless operation- No
webchatchannel - the Control UI is served automatically gateway.bindis not a config option - use--bindCLI flag
See Moltbot docs for full schema.
- Add route handler in
src/routes/api.ts - Add types if needed in
src/types.ts - Update client API in
src/client/api.tsif frontend needs it - Add tests
- Add to
MoltbotEnvinterface insrc/types.ts - If passed to container, add to
buildEnvVars()insrc/gateway/env.ts - Update
.dev.vars.example - Document in README.md secrets table
# View live logs
npx wrangler tail
# Check secrets
npx wrangler secret listEnable debug routes with DEBUG_ROUTES=true and check /debug/processes.
R2 is mounted via s3fs at /data/moltbot. Important gotchas:
-
rsync compatibility: Use
rsync -r --no-timesinstead ofrsync -a. s3fs doesn't support setting timestamps, which causes rsync to fail with "Input/output error". -
Mount checking: Don't rely on
sandbox.mountBucket()error messages to detect "already mounted" state. Instead, checkmount | grep s3fsto verify the mount status. -
Never delete R2 data: The mount directory
/data/moltbotIS the R2 bucket. Runningrm -rf /data/moltbot/*will DELETE your backup data. Always check mount status before any destructive operations. -
Process status: The sandbox API's
proc.statusmay not update immediately after a process completes. Instead of checkingproc.status === 'completed', verify success by checking for expected output (e.g., timestamp file exists after sync).