Waffle-bot provides an AI-powered chat service over Telegram by running a containerized OpenClaw gateway behind a Cloudflare Worker.
| Runtime | Cloudflare Workers + Containers |
| AI Engine | OpenClaw Gateway |
| LLM Provider | Z.ai (zai/glm-4.7) |
| Channel | Telegram Bot |
| Storage | R2 (media/files) Β· KV (config cache) |
| URL | https://waffle-bot.ytu-iremkurt.workers.dev |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Cloudflare Edge β
β β
β ββββββββββββββββ βββββββββββββββββββββββββββββ β
β β Worker ββββββββΆβ Durable Object (Container)β β
β β (src/index) β β OpenClawContainer β β
β β β β β β
β β β’ /ping β β βββββββββββββββββββββββ β β
β β β’ /health β β β OpenClaw Gateway β β β
β β β’ /health/ β β β (Node 22, port 8080)β β β
β β startup β β β β β β
β β β’ /* proxy β β β βββ Telegram Bot βββ β β
β ββββββββββββββββ β βββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββ ββββββββββββββββ β
β β R2 Bucket β β KV Namespace β β
β β waffle-storageβ β WAFFLE_KV β β
β ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Telegram β Webhook message arrives at Cloudflare Worker.
- Worker β Proxies the request to the container inside Durable Object (
container.fetch(request)). - Container β OpenClaw gateway receives the message, sends it to Z.ai LLM, and returns the response to Telegram.
team/waffle/
βββ src/
β βββ index.ts # Worker + OpenClawContainer (Durable Object)
βββ Dockerfile # Container image definition (Node 22-slim)
βββ entrypoint.sh # Container startup script
βββ wrangler.jsonc # Cloudflare Workers configuration
βββ package.json # Project dependencies
βββ tsconfig.json # TypeScript configuration
βββ README.md # This file
The Worker serves two main functions:
- Health check endpoints β Can run independently of the container.
- Proxy β Forwards all other incoming requests to the container.
| Property | Value | Description |
|---|---|---|
defaultPort |
8080 |
Port the container listens on |
sleepAfter |
"30m" |
Enters sleep mode after 30 minutes of inactivity |
enableInternet |
true |
Container can access the external network |
pingEndpoint |
"/health" |
Liveness check endpoint |
The container transfers environment variables from Worker secrets to the runtime in the constructor:
TELEGRAM_BOT_TOKENZAI_API_KEYOPENAI_BASE_URLMOLTBOT_GATEWAY_TOKEN
Hooks:
onErrorβ Logs container errors.onStopβ Logs the exit code and reason when the container stops.
- Base image:
node:22-slim - Includes Chromium libraries (for OpenClaw internal dependencies).
openclaw@latestis installed from npm.entrypoint.shgenerates theopenclaw.jsonconfig file at runtime and starts the gateway.
| Binding | Type | Usage |
|---|---|---|
WAFFLE_STORAGE |
R2 Bucket | Media and file storage |
WAFFLE_KV |
KV Namespace | Configuration cache |
- Node.js β₯ 18
wranglerCLI (npm install -g wrangleror available as project devDependency)- Cloudflare account (Workers Paid plan β required for Containers)
npm installnpx wrangler secret put TELEGRAM_BOT_TOKEN
npx wrangler secret put ZAI_API_KEY
npx wrangler secret put OPENAI_BASE_URL
npx wrangler secret put MOLTBOT_GATEWAY_TOKEN
β οΈ Secret values are not added to version control. Each secret must be set separately via the Cloudflare dashboard or CLI.
npm run deploy
# or
npx wrangler deployWrangler will build the container image via the Dockerfile, upload it to Cloudflare, and deploy the Worker.
The container will be asleep after the first deployment. To wake it up and check its health:
# Fast ping (without starting the container)
curl -sS https://waffle-bot.ytu-iremkurt.workers.dev/ping
# β waffle is live
# Start the container and check status
curl -sS https://waffle-bot.ytu-iremkurt.workers.dev/health/startup
# β {"ok":true,"container":"waffle-main-v2","state":{"status":"healthy",...},...}
# If already running, just query status
curl -sS https://waffle-bot.ytu-iremkurt.workers.dev/health
# β {"ok":true,"container":"waffle-main-v2","state":{"status":"healthy",...},...}| Endpoint | Method | Container Required? | Description |
|---|---|---|---|
/ping |
GET | β | Worker liveness check. Returns fixed "waffle is live". |
/health |
GET | β | Queries container state (does not start it). |
/health/startup |
GET | β | Starts the container, waits until ports are ready, then returns state. 45s timeout. |
{
"ok": true,
"container": "waffle-main-v2",
"state": {
"status": "healthy",
"lastChange": 1771785038347
},
"timestamp": "2026-02-22T18:30:38.414Z"
}{
"ok": false,
"container": "waffle-main-v2",
"error": "Timeout waiting for container ports",
"timestamp": "..."
}npm run dev
# Starts wrangler dev β emulates the container locallyNote:
wrangler devcontainer support is limited; some features can only be tested after deployment.
| Field | Value | Description |
|---|---|---|
name |
"waffle" |
Worker name |
main |
"src/index.ts" |
Entry point |
compatibility_date |
"2026-02-22" |
Cloudflare compatibility date |
containers[0].class_name |
"OpenClawContainer" |
DO/Container class name |
containers[0].image |
"./Dockerfile" |
Container image source |
containers[0].instance_type |
"standard-1" |
Instance type |
containers[0].max_instances |
2 |
Max concurrent container instances |
| Secret | Description |
|---|---|
TELEGRAM_BOT_TOKEN |
Bot token received from Telegram BotFather |
ZAI_API_KEY |
Z.ai API access key |
OPENAI_BASE_URL |
Z.ai/OpenAI compatible API base URL |
MOLTBOT_GATEWAY_TOKEN |
OpenClaw gateway auth token |
| Issue | Possible Cause | Solution |
|---|---|---|
1101: The script will never generate a response |
Unhandled exception in Worker or missing try/catch | Check logs with wrangler tail |
The container is not running, consider calling start() |
Container is in sleep mode or has crashed | Wake up with /health/startup |
unauthorized: gateway token missing |
MOLTBOT_GATEWAY_TOKEN secret is missing or incorrect |
Re-set the secret: wrangler secret put MOLTBOT_GATEWAY_TOKEN |
No API key found for provider "anthropic" |
Model provider mismatch | Ensure Z.ai provider is used (zai/glm-4.7) |
| Container constantly restarts | Missing environment variable | Verify mandatory secret checks in entrypoint.sh |
- Sleep Mode: The container automatically goes to sleep after 30 minutes of inactivity. The first request may wait for the cold start duration (~45s).
- Instance Limit: A maximum of 2 concurrent container instances can run.
- Log Monitoring:
npx wrangler tailcan be used for live logs. - Telegram Group Behavior: To respond to group messages, check OpenClaw
groupPolicyconfiguration and BotFather privacy mode settings. This feature is not yet stable. - DM Policy: Open to all direct messages (
"dmPolicy": "open").
| Date | Version / Deploy | Notes |
|---|---|---|
| 2026-02-22 | waffle-main-v2 |
Gateway token, Z.ai provider integration, health endpoints, error management improvements. Stable DM mode. |
Private project β internal use.
{ "channels": { "telegram": { "enabled": true, "allowFrom": ["*"], "accounts": { "waffle": { "name": "Waffle-bot", "enabled": true, "botToken": "..." } }, "dmPolicy": "open" } }, "agents": { "defaults": { "model": { "primary": "zai/glm-4.7" } } }, "gateway": { "mode": "local", "port": 8080, "auth": { "token": "..." } } }