Install
openclaw skills install openclaw-tg-canvasTelegram Mini App Canvas with terminal and optional Control UI proxy. Renders agent-generated content (HTML, markdown, A2UI) in a Telegram Mini App; push with `tg-canvas push`. Includes a JWT-gated browser terminal (server-side PTY/bash — high privilege, shell access to server). Optionally proxies OpenClaw Control UI via ENABLE_OPENCLAW_PROXY=true (off by default; no local files read).
openclaw skills install openclaw-tg-canvasQ: Does the server auto-load ~/.openclaw/openclaw.json or any local credential file?
No. The server reads no local files for credentials. OPENCLAW_GATEWAY_TOKEN must be supplied explicitly via environment variable. The relevant code is the ENABLE_OPENCLAW_PROXY config block in server.js — verify it yourself.
Q: What is the default for ENABLE_OPENCLAW_PROXY?
Off. The code is const ENABLE_OPENCLAW_PROXY = process.env.ENABLE_OPENCLAW_PROXY === "true"; — it is only enabled if the string "true" is explicitly set in the environment. Omitting the variable leaves it disabled.
Q: What are the terminal/PTY endpoints and how are they authenticated?
GET /ws/terminal (WebSocket upgrade)verifyJwt() in the upgrade handler — same token issued by POST /auth after Telegram initData HMAC-SHA256 verification against BOT_TOKEN, restricted to ALLOWED_USER_IDS401 Unauthorized before a PTY is spawnedThis is a server skill. It includes a Node.js HTTP/WebSocket server (server.js), a CLI (bin/tg-canvas.js), and a Telegram Mini App frontend (miniapp/). It is not instruction-only.
Telegram Mini App Canvas renders agent-generated HTML or markdown inside a Telegram Mini App, with access limited to approved user IDs and authenticated via Telegram initData verification. It exposes a local push endpoint and a CLI command so agents can update the live canvas without manual UI steps.
cloudflared for HTTPS tunnel (required by Telegram Mini Apps).env file.BOT_TOKEN=... MINIAPP_URL=https://xxxx.trycloudflare.com node scripts/setup-bot.js
node server.js
cloudflared tunnel --url http://localhost:3721
tg-canvas push --html "<h1>Hello</h1>"
tg-canvas push --markdown "# Hello"
tg-canvas push --a2ui @./a2ui.json
curl -X POST http://127.0.0.1:3721/push \
-H 'Content-Type: application/json' \
-d '{"html":"<h1>Hello</h1>"}'
What the Cloudflare tunnel exposes publicly:
| Endpoint | Public? | Auth |
|---|---|---|
GET / | ✅ | None (serves static Mini App HTML) |
POST /auth | ✅ | Telegram initData HMAC-SHA256 verification + ALLOWED_USER_IDS check |
GET /state | ✅ | JWT required |
GET /ws | ✅ | JWT required (WebSocket upgrade) |
POST /push | ❌ loopback-only | PUSH_TOKEN required + loopback check |
POST /clear | ❌ loopback-only | PUSH_TOKEN required + loopback check |
GET /health | ❌ loopback-only | Loopback check only (read-only, low risk) |
GET/WS /oc/* | ✅ (when enabled) | JWT required; only available when ENABLE_OPENCLAW_PROXY=true |
⚠️ Cloudflared loopback bypass:
cloudflared(and other local tunnels) forward remote requests by making outbound TCP connections tolocalhost. This means all requests arriving via the tunnel appear to originate from127.0.0.1at the socket level — completely defeating the loopback-only IP check.PUSH_TOKENis therefore required and is enforced at startup. The loopback check is retained as an additional layer but must not be relied on as the sole protection.
Recommendations:
PUSH_TOKEN — the server will refuse to start without it. Generate one with: openssl rand -hex 32JWT_SECRET (32+ bytes).BOT_TOKEN, JWT_SECRET, and PUSH_TOKEN secret; rotate if compromised.ALLOWED_USER_IDS check in /auth is the primary access control gate for the canvas.ENABLE_OPENCLAW_PROXY is off by default. Only enable it if you need Control UI access through the Mini App and understand the implications (see below).The server can optionally proxy /oc/* to a local OpenClaw gateway, enabling you to access the OpenClaw Control UI through the Mini App.
This feature is disabled by default. To enable:
ENABLE_OPENCLAW_PROXY=true
When enabled, the server:
/oc/* HTTP and WebSocket requests to the local OpenClaw gateway.OPENCLAW_GATEWAY_TOKEN is set, injects it as Authorization: Bearer on proxied requests.The server does not read any local files for credentials — OPENCLAW_GATEWAY_TOKEN must be supplied explicitly via environment variable if needed.
When using /oc/* over a public origin, add that origin to OpenClaw gateway config:
{
"gateway": {
"controlUi": {
"allowedOrigins": ["https://your-canvas-url.example.com"]
}
}
}
The Mini App includes an interactive terminal backed by a server-side PTY.
⚠️ This grants shell access to the machine running the server, as the process user. Anyone in
ALLOWED_USER_IDScan open a bash session and run arbitrary commands. Only add users you trust with shell access toALLOWED_USER_IDS.
How it works:
/ws/terminal (JWT required).Runtime scope: node-pty spawns a bash process as the server process user. No additional env vars control this; auth is the only gate.
tg-canvas push — push HTML/markdown/text/A2UItg-canvas clear — clear the canvastg-canvas health — check server health| Variable | Required | Default | Description |
|---|---|---|---|
BOT_TOKEN | Yes | — | Telegram bot token for API calls and initData verification. |
ALLOWED_USER_IDS | Yes | — | Comma-separated Telegram user IDs allowed to authenticate. Controls access to canvas, terminal, and proxy. |
JWT_SECRET | Yes | — | Secret for signing session JWTs. Use 32+ random bytes. |
PUSH_TOKEN | Yes | — | Shared secret for /push and /clear. Server refuses to start without it. Generate: openssl rand -hex 32 |
MINIAPP_URL | Yes (setup only) | — | HTTPS URL of the Mini App, used by scripts/setup-bot.js to configure the bot menu button. |
PORT | No | 3721 | HTTP server port. |
TG_CANVAS_URL | No | http://127.0.0.1:3721 | Base URL used by the tg-canvas CLI. |
ENABLE_OPENCLAW_PROXY | No | false | Set to the string "true" to enable /oc/* proxy to a local OpenClaw gateway. Off by default. The server does not read any local files to obtain a token — OPENCLAW_GATEWAY_TOKEN must be set explicitly if auth is needed. |
OPENCLAW_GATEWAY_TOKEN | No | (unset) | Auth token injected as Authorization: Bearer on proxied /oc/* requests. Only used when ENABLE_OPENCLAW_PROXY=true. Must be supplied explicitly; no automatic file loading occurs. |
OPENCLAW_PROXY_HOST | No | 127.0.0.1 | Hostname of the local OpenClaw gateway (proxy only). |
OPENCLAW_PROXY_PORT | No | 18789 | Port of the local OpenClaw gateway (proxy only). |
JWT_TTL_SECONDS | No | 900 | Session token lifetime in seconds (default 15 min). |
INIT_DATA_MAX_AGE_SECONDS | No | 300 | Maximum age of Telegram initData (default 5 min). |