The Dev.to series describes how to run a coding agent from a phone via a Telegram topic using OpenClaw, which relays messages to a specific tmux pane on a controlled host. Part 2 focuses on getting an agent answering: it pins the runtime (Node.js 24.11.1) and package manager (pnpm 11.2.2), builds and launches the OpenClaw gateway in a persistent tmux session, and configures Telegram by creating a bot, adding it as a group admin, enabling Topics, and mapping topic thread IDs to a dedicated agent ID. It emphasizes a readiness gate that verifies end-to-end behavior, including that the bot receives messages, that “status” gets a response, that a real instruction reaches the pane, that the coding agent visibly starts a turn, that topic routing creates session artifacts, and that the group is locked down using an allowlist. Part 3 then defines the “operating contract” for day-to-day control in the Telegram topic. Certain words are handled locally by the relay (e.g., status, send <message>, compact/new session, interrupt/stop, restart). All other text is forwarded to the coding agent. It also clarifies how to answer option prompts from the agent (e.g., A/B or yes/no) so the reply is sent to the pane rather than being interpreted by the relay. Finally, it lists safety constraints, including read-only handling for production and staging, restrictions on merges/pushes, and a rule to never paste secrets into the relay topic.
OpenClaw Telegram Coding Agents: relay command contract, setup steps, and safety rules
The Dev.to series describes how to run a coding agent from a phone via a Telegram topic using OpenClaw, which relays messages to a specific tmux pane on a controlled host. Part 2 focuses on getting an...
- OpenClaw uses a Telegram topic to relay instructions to a pinned tmux pane running a coding agent, with a local command vocabulary and a forwarded default.
- The gateway/setup pins Node.js 24.11.1 and pnpm 11.2.2, builds OpenClaw, and runs it via a persistent tmux session; configuration hot-reloads for routing changes.
- Telegram configuration requires promoting the bot to group admin and enabling Topics; the system logs chat and topic thread IDs to route a specific topic to an agent ID.
- The readiness gate verifies bot responsiveness (“status”), that prompts are injected into the tmux pane, that the coding agent starts working, that routing produces session artifacts, and that the allowlist is enforced.
- Operating safety rules include never sending mutations for production/staging, not pushing/merging shared branches, and never pasting secrets into the relay topic.
You finished Part 2, so you have a topic where you type a message and a coding agent answers and drives a pane. This post is the operating contract: the small vocabulary the relay understands, the one routing rule that confuses everyone the first day, and (because this is a phone wired to a shell) the short list of things you must never send. It's a contract, not a tutorial; read it once and you'll drive the thing confidently. Keep the mental model from Part 1 in front of you: you are talking through a relay agent to a coding agent in a tmux pane. The relay is a courier. Most of what you type is forwarded straight to the pane; a handful of words are handled by the relay itself. The words the relay handles directly These are local control: the relay acts on them instead of forwarding them. They come from the relay's AGENTS.md (the command table you wrote in Part 2), so the exact phrasings are yours to tune: You type What happens status / what's in tmux? Relay captures the pane, strips the terminal formatting, and summarizes what the agent is doing right now send <message> Relay types <message> into the pane and submits it compact / new session Relay sends the agent's compaction / new-session command interrupt / stop it Relay interrupts the running turn (it confirms first) restart Relay cleanly exits and relaunches the coding agent, resuming the same session (no confirmation) status is the one you'll lean on most. Until you add the automatic monitors in Part 4, status is how you check in: there are no progress pings yet, so you ask. One caveat to hold onto: status shows you what's on the pane right now; it's a snapshot, not proof. It can report that the agent claims the tests passed; it can't tell you they actually did. Treating that claim as something to verify rather than trust is exactly what Part 5 is about. Which control, when? Reach for interrupt when the agent is doing the wrong thing or about to take an unsafe step: you want to stop this turn and redirect. Reach for restart only when the pane or session is genuinely wedged; it resumes the same session, so you keep your context. Use compact when the conversation is getting long but you want to keep going, and new session to start a fresh, unrelated task. Everything else goes to the agent Anything that isn't one of those control words is forwarded verbatim into the pane. "Add a retry to the upload client and run the tests" isn't a command the relay interprets; it's an instruction for the coding agent, so the relay just types it in and lets the agent work. That's the default, and it's most of what you do. Treat that first message like a good ticket: name the repo and branch, the change you want, the test expectation, and any constraint. The agent acts on what you give it, so a precise prompt beats a chatty one. (Need to send a literal control word to the agent, say the actual text status? Use send status, which types it into the pane instead of triggering the relay's own status.) The one gotcha: option replies belong to the agent This is the single thing that confuses everyone on day one, so internalize it now. When the coding agent (inside the pane) asks you something (prints A / B options, asks Proceed?, or yes/no), your short reply is an answer for the agent, and the relay forwards it verbatim. It does not act on it itself. After the agent shows options, typing: A, B, yes, no, do it, sorry, B → goes straight to the pane as your choice. That's what you want: you're answering the coding agent's question, not issuing a new command to the relay. The reason this needs stating is the failure mode it prevents: without this rule, a bare A could be misread as an instruction to the relay, and it might go act on "option A" itself instead of passing your choice through. If you ever genuinely want the relay to make the choice or do something local, address it explicitly: you pick A or don't send this to the agent, just tell me X. Explicit address is the override. Part 4 hardens this with a deterministic router so the classification is instant and never guesses. In your Part 2 setup it's handled by the relay's own instructions, which is enough as long as you remember the rule above. Know which agent you're talking to Topics map to agents, and not all agents are equal: A relay agent (what you built in Part 2) is pinned to one pane: by itself it can only nudge that agent, read the pane, and restart it, nothing more. But be clear-eyed: the relay is not a security boundary. The coding agent in that pane can still do whatever its own local permissions allow; the pin constrains the relay, not the agent's reach, which is why the rules below matter. An ops agent (covered later) is not pinned. It runs commands across the whole box and owns sensitive jobs like credential refreshes. It is far more powerful, and you treat its topic with more care precisely because there's no pane boundary containing it. For pre-work you have exactly one relay and one pane. Just be aware that "which topic am I in?" determines "how much can this agent do?" That's a habit worth forming before you add an ops topic. What you must never type This is Telegram talking to a shell. The relay enforces guardrails (you encoded the first ones in its AGENTS.md), but you are the first line of defense. The contract: Production and staging are read-only. Never ask the agent to apply, delete, edit, patch, scale, or restart anything in prod or staging. Inspect and debug only. No pushes or merges to shared branches. Feature and bugfix branches are fine; main / dev / GitOps branches are human-merge-only. A merge to a GitOps branch is a deploy. Shared infrastructure needs a human. Gateways, API gateways, and anything used by other teams: no mutations without explicit human approval. Never paste secrets into a relay topic. Credentials are an ops-agent concern, and even there they go in via a guarded path and are never echoed or relayed. A relay topic is the wrong place, full stop. If the relay refuses an instruction or asks you to confirm, don't fight it; that's the contract working. Right now that contract is the explicit rails in your agent's AGENTS.md plus your own operator discipline; the series' final piece (the supervisor) makes the judgment automatic and harder to bypass. A day in the life What this actually feels like, with only your Part 2 setup: From the queue at a coffee shop, you open the topic and type a task: "In the upload service, add exponential backoff to the S3 client and run the unit tests." The relay forwards it; the agent starts. A few minutes later you type status. The relay summarizes: tests running, two failing. The agent asks whether to bump the retry ceiling to 5 or 10. You reply 5. It goes to the pane; the agent continues. You type status again: the pane reports the tests green. You ask the agent to open a feature branch and push, and it does. It got wedged once on a stale process; you typed restart and it came back on the same session. No terminal, no SSH, the whole loop from your phone. In Part 4 you'll add monitors that push progress and a final summary automatically, so you stop having to poll with status. But the loop above already works today. Before the session You're set if you can still tick the Part 2 readiness gate: The bot replies to status in your topic. A real instruction reaches the pane and the agent acts on it. Routing is correct (your agent handled your topic). The group is locked down: allowlist, your ID only, no @mention required. You can drive the loop above end to end. Bring a redacted screenshot of that working loop as your "I'm ready" artifact. If you're blocked, send your symptom plus Node/pnpm versions and the last few (redacted) gateway log lines so it's sorted before we're in the room. That's the end of the pre-work. In the live session we go beyond a single courier: Part 4 makes agents genuinely capable (loadable skills, tool servers, per-topic memory, and the monitors that notify you automatically), and Part 5 is the payoff: the skeptical supervisor that audits an agent's evidence and blocks the confident-but-wrong answer before it reaches you.
10 hours agoThis is the one post in the series you do, not just read. By the end you'll have a single Telegram topic where you type a message and a coding agent answers and drives a tmux pane on your own box. That's the entire goal, nothing more. Memory, monitors, tool servers, and the supervisor all come later; none of them are needed to get an agent answering you. Budget ~30–45 minutes. If you only do one thing, do the Fast path, then prove it with the readiness gate. Everything below the gate is manual explanation and debugging you can skip until you need it. Prerequisites (where solo setups die) The Telegram wiring is the mechanical part; the real failures hide in local state. Confirm every line before you start: A box you control with tmux, and shell access that survives disconnects (you'll leave a gateway running). A coding agent already working in a tmux pane (opencode, Codex, or Claude Code) that you can drive by hand right now. If it isn't installed and authenticated yet, stop and fix that first; OpenClaw drives it, it doesn't replace it. A known, stable pane target for that agent (for example, mybox:1.1). Write it down; you'll hard-code it into the agent's instructions. Your agent's launch command: the exact command (or wrapper script) that starts the coding agent in the pane, including how it resumes a session. OpenClaw's restart flow needs this verbatim. The OpenClaw runtime, pinned (see the matrix below). Wrong Node version is the single most common silent failure. A Telegram account and the Telegram app on your phone. Version matrix Pin these. "It runs on Node" is not enough; the gateway is sensitive to the runtime. Component Pinned version Notes Node.js 24.11.1 The gateway is built against Node 24; newer majors can fail the native build. Package manager pnpm 11.2.2 OpenClaw's packageManager field. corepack fetches it for you. OpenClaw github.com/openclaw/openclaw, pinned commit (tested on 2026.5.27) Pin a commit or tag; don't track main for a setup everyone must reproduce. Coding agent opencode, installed and authenticated Pin the version your team standardizes on. Node manager nvm This guide assumes nvm; adapt the commands if you use asdf or system Node. Fast path Two scripts do the whole local setup. Grab them from the gist, then run them around the Telegram steps: # Download the bootstrap + readiness scripts curl -fsSL "https://gist.githubusercontent.com/jerilkuriakose/cd0f8353aac74e47c591111b758943e9/raw/setup-openclaw.sh" -o setup-openclaw.sh curl -fsSL "https://gist.githubusercontent.com/jerilkuriakose/7cf94af3e96526f9f14d0c28b6c26b69/raw/ready-check.sh" -o ready-check.sh chmod +x setup-openclaw.sh ready-check.sh # 1. Pin the runtime, fetch + build OpenClaw, and launch the gateway. # Pass your bot token to also write a minimal config in one shot: OPENCLAW_BOT_TOKEN="<BOT_TOKEN>" OPENCLAW_BOT_ACCOUNT="my-bot" ./setup-openclaw.sh # 2. Do the Telegram steps in "Telegram side" below (BotFather + group + topic), # then add the allowlist + topic route from "Wire them together". # 3. Prove it works: AGENT_ID=my-agent PANE=mybox:1.1 ./ready-check.sh setup-openclaw.sh pins Node 24.11.1 and pnpm 11.2.2 (via nvm and corepack), clones and builds github.com/openclaw/openclaw, and launches the gateway. ready-check.sh runs the readiness gate for you. Prefer to understand each step, or hit a snag? Follow the manual path. Manual path Three parts: the Telegram side, the box side, then wiring them together. Telegram side You must be the group's creator, so do these from your own Telegram account: Create a bot. Message @BotFather → /newbot → name it → save the bot token it gives you. Treat the token like a password (see Secrets). Create a group, give it a name, and add your bot to it. Promote the bot to admin (Group → Edit → Administrators). This is required: Telegram's privacy mode hides normal group messages from non-admin bots, so without this the bot never sees what you type. Turn Topics on (Group → Edit → Topics). This converts the group to a forum supergroup. Create your first topic and name it for the project the agent will drive (for example, project-a). Send one message in that topic (anything). This makes the group and topic show up in the gateway log so you can grab their IDs. You also need your own numeric Telegram user ID for the allowlist. Easiest: message @userinfobot and it replies with your ID. (Alternatively, it appears in the gateway log as the sender once messages start flowing in the next step.) Box side: minimal config, then start the gateway The gateway has to know about your bot before it can poll Telegram, so write a minimal valid config first. Create ~/.openclaw/openclaw.json with just the bot account. Use strict JSON (no comments, no trailing commas) because that's exactly what the gateway parses and what you'll validate against: { "channels": { "telegram": { "enabled": true, "accounts": { "<your-bot-account>": { "botToken": "<BOT_TOKEN>" } } } } } Lock it down and validate before launching: chmod 600 ~/.openclaw/openclaw.json python3 -c "import json; json.load(open('$HOME/.openclaw/openclaw.json')); print('JSON OK')" Get OpenClaw and build it. It's open source. Pin the runtime, clone, install with the corepack-provided pnpm, and build: nvm install 24.11.1 && nvm use 24.11.1 corepack enable && corepack prepare pnpm@11.2.2 --activate git clone https://github.com/openclaw/openclaw.git ~/repos/openclaw cd ~/repos/openclaw # Optional: pin a tested commit/tag for reproducibility (tested on 2026.5.27) # git checkout <commit-or-tag> pnpm install pnpm build # takes a few minutes Now launch the gateway in its own tmux session so it survives your disconnect: pnpm gateway:watch # launches the gateway and manages its own tmux session Find your log path (it differs by config), then confirm the gateway came up and the bot is polling: # If logging.file is set, use ~/.openclaw/logs/openclaw.log. Otherwise the dated default: ls -t /tmp/openclaw/openclaw-*.log | tail -1 # default location grep -a 'gateway ready' <your-log-path> | tail -1 # expect a recent line The log directory is locked down (0700), so read it from the shell, not an editor's file browser. This initial launch, and any later logging or plugins.load change, needs a gateway (re)start. The group/topic/routing edits in the next section hot-reload, no restart needed. Wire them together Config lives in ~/.openclaw/openclaw.json. The gateway hot-reloads routing/topic/channel edits; no restart needed for these. We do this in two phases because you need the chat and topic IDs from the log, and the only way to make them appear is to let messages through first. Phase 1: find the chat ID and open the group temporarily. With the gateway now polling, send a message in your topic. It gets blocked (the group isn't configured yet), which conveniently logs the chat ID: grep -a 'not-allowed' <your-log-path> | tail -1 # → {"chatId":<CHAT_ID>,"title":"<your group>","reason":"not-allowed"} Add a groups block to your account so messages flow and topic IDs get logged. This is a fragment; merge the groups key into the account you already created, keeping the file strict JSON: "groups": { "<CHAT_ID>": { "groupPolicy": "open", "requireMention": false } } Save. The gateway hot-reloads (no restart). Keep the open window tiny. groupPolicy: "open" lets anyone in the group drive a shell-capable agent. The group must be private with only you in it, and this is a momentary bootstrap step: switch to allowlist (Phase 2) as soon as you've harvested the topic IDs, before adding anyone else or doing real work. Phase 1b: harvest topic thread IDs. Send a message in each topic (label them by text so you can tell them apart, since topic IDs are not sequential by creation order), then: grep -ao 'Inbound message telegram:group[^"]*' <your-log-path> | sort -u # → ...:topic:<TOPIC_THREAD_ID> Phase 2: lock it down and route the topic to an agent. Now switch the group to an allowlist (owner-only), add yourself, map the topic to an agent, and declare that agent. Strict JSON, merged into your config: "channels": { "telegram": { "enabled": true, "accounts": { "<your-bot-account>": { "botToken": "<BOT_TOKEN>", "groups": { "<CHAT_ID>": { "groupPolicy": "allowlist", "allowFrom": ["<YOUR_TELEGRAM_USER_ID>"], "requireMention": false, "topics": { "<TOPIC_THREAD_ID>": { "agentId": "<your-agent-id>" } } } } } } } }, "agents": { "list": [ { "id": "<your-agent-id>", "workspace": "~/.openclaw/workspace-<name>", "agentDir": "~/.openclaw/agents/<name>" } ] } requireMention: false lets you type naturally instead of prefixing every message with an @mention. allowFrom with only your user ID is what stops anyone else in the group from driving a shell-capable agent. It's your numeric Telegram ID, kept as a quoted string as the config expects (for example, ["123456789"]). Give the agent its instructions. This is the step that makes status, send, and restart work; the agent's behavior comes entirely from the files in its workspace. Create the workspace directory first (the agentDir must be unique per agent; the gateway creates it on first run): mkdir -p ~/.openclaw/workspace-<name> Then put this in ~/.openclaw/workspace-<name>/AGENTS.md, a minimal command contract: # <your-agent-id> You are bound to tmux pane `<PANE>`, an interactive coding-agent session. ## Hard rules - ALL tmux operations target `<PANE>`. Never touch any other session. - Read with `tmux capture-pane -t <PANE> -p`; write with `tmux send-keys -t <PANE> ...`. - Strip ANSI codes before relaying pane output to Telegram. - After send-keys, wait a few seconds before re-capturing; replies aren't instant. - Confirm before any destructive action EXCEPT the explicit "restart" command below. ## Common phrasings → actions | User says | You do | |---|---| | "status" / "what's in tmux?" | capture `<PANE>`, strip ANSI, summarize the last ~60 lines | | "send `<msg>`" | `send-keys -t <PANE> -l -- "<msg>"`, then Enter, wait, capture | | "compact" / "new session" | send `/compact` or `/new` to the pane | | "interrupt" / "stop it" | confirm, then send `C-c` | | "restart" | run the restart sequence below (no confirmation) | ## Decision / option routing You are a relay, not the owner of the work in the pane. If your previous reply summarized options/questions from the agent (e.g. "A/B", "yes/no", "Proceed?"), then short replies like `A`, `B`, `yes`, `no`, `do it`, `sorry A` are answers **for the pane**: forward them verbatim; do not act on them yourself. Only act locally when explicitly addressed ("you do option A"). ## Restart sequence (no confirmation) 1. Resolve the exact session id first (do not blindly "continue", which can reopen the wrong session). 2. Exit the agent's UI cleanly and wait until you actually see a shell prompt. 3. Relaunch with EXACTLY this command, resuming that session id: `<your-launch-command> --resume <session_id>` 4. Capture and report whether it came back up. ## Safety rails (applied to anything you relay) - Production: strict read-only. Never relay mutations. - Never relay pushes/merges to shared branches. - Shared infra (gateways, API gateways): never mutate without explicit human approval. Replace <PANE> and the restart command with your exact values; leave them vague and the agent will improvise and can lose your session. This single file is your agent; the workspace can contain just AGENTS.md to start. Required: validate before you trust the save. An invalid openclaw.json is dangerous: on a restart the gateway rejects it, auto-restores the last-known-good, and the watch process exits (that is, an outage). Editing a running gateway is safer (a bad edit is reverted with no downtime), but never restart on an unvalidated config. Every time: # 1. JSON is parseable python3 -c "import json; json.load(open('$HOME/.openclaw/openclaw.json')); print('JSON OK')" # 2. Dry-run the config against a temp copy (never touches the live gateway). # The copy holds your bot token; keep it 0600 and delete it after. cp ~/.openclaw/openclaw.json /tmp/oc-check.json && chmod 600 /tmp/oc-check.json OPENCLAW_CONFIG_PATH=/tmp/oc-check.json pnpm openclaw doctor --non-interactive # expect Errors: 0 rm -f /tmp/oc-check.json # 3. Confirm the hot reload landed grep -a 'config hot reload applied' <your-log-path> | tail -3 Readiness gate You are not done when the config saves. You're done when you have evidence the whole path works. Verify all five; this is exactly what you'll confirm to the session organizer: The bot replies in the topic. Type status in your topic; you get a response (not silence). A prompt reaches the pane. Send a real instruction, then check the pane received it: tmux capture-pane -t <PANE> -p | tail -n 40 You should see your text injected into the coding agent. The pane actually moved: concretely, the coding agent picked up the prompt and started a turn (you see it thinking/streaming, or a new command running), not just your text sitting at an input line. Routing is correct. A session/log artifact proves your agent handled your topic: ls -t ~/.openclaw/agents/<name>/sessions/*topic-<TOPIC_THREAD_ID>* 2>/dev/null It's locked down. Final config shows groupPolicy: "allowlist", your ID in allowFrom, and requireMention: false; you're no longer in open mode from Phase 1. If all five hold, you're ready for the session. Capture a redacted screenshot/log snippet of #1–#2 as your "I'm ready" artifact, and redact the log excerpt too (it carries chat IDs, thread IDs, your user ID, and message text), not just the screenshot. If it breaks Recovery first. If a restart took the gateway down, your edit wasn't lost; the gateway auto-restored the last-known-good and saved your version alongside it. Recover from ~/.openclaw/openclaw.json.clobbered.* (also check .last-good / .bak), fix the cause, validate (above), then restart: nvm use 24.11.1 tmux kill-session -t openclaw-gateway-watch-main cd ~/repos/openclaw && pnpm gateway:watch grep -a 'gateway ready' <your-log-path> | tail -1 Symptom Cause Fix Bot silent; log shows "reason":"not-allowed" Group not configured, or you're not in allowFrom Add the group; put your numeric ID in allowFrom (or use open while testing) Bot only replies when @mentioned requireMention defaulting to true Set requireMention: false Bot doesn't see messages at all Telegram privacy mode on a non-admin bot Make the bot a group admin Topic IDs never appear in the log Messages blocked before topic resolution Open the group (Phase 1) first, then re-send getUpdates 409 Conflict Same bot token polled by two gateways One gateway per token; kill the duplicate poller Config edit "vanished" / gateway down after restart Invalid JSON at startup → auto-restore → watch exits Recover from .clobbered.*; validate; restart Agent replies but the pane does nothing Wrong <PANE>, or the coding agent isn't running there Fix the pane target; relaunch the agent in that pane Secrets This is a public-internet bot with shell reach. Treat it that way: The bot token lives in openclaw.json. chmod 600 it, never commit it, never screenshot it. If a token leaks, rotate it via @BotFather. Keep the group private: just you and the bot. The owner-only allowlist is the gate; anyone you add to the group still can't drive the bot unless their ID is in allowFrom. When you blog/screenshot/share: redact bot handles, chat IDs, your user ID, hostnames, and paths, including log excerpts, which carry all of those plus raw message text. Credentials (cloud, registry) are an ops-agent concern, not this relay's. Never paste secrets into this topic, and never let an agent echo or relay them. Out of scope (deliberately deferred) You do not need any of this to finish the readiness gate; it all comes in Part 4 / the session: Per-topic semantic memory and any native embedding build Progress/completion notification daemons Tool servers (MCP) for the agent Loadable skills The fast deterministic intent router (your AGENTS.md already handles status/send/restart/option replies prompt-driven; the router just makes it faster and stricter) The ops agent (box-wide shell). One relay + one pane is the whole assignment. Before the session 24 hours out, confirm you can tick all five readiness items and post your redacted "ready" artifact. If you're blocked, send your symptom plus your Node/pnpm versions and the last few gateway log lines (redacted) so it can be sorted before we're all in the room. Part 3 is the operating contract: what to type, what not to type, and how to supervise the agent safely once it's answering you.
10 hours ago
Steve Clarke hails Scotland’s must-win as they beat Haiti 1-0
Scotland manager Steve Clarke says Scotland treated their World Cup Group C opener against Haiti as a “must-win game” af...
Wings 999 vs Vital Tea: President Trophy Grade-II (Gold) continues across two days
Wings 999 play Vital Tea in the President Trophy Grade-II (Gold) tournament, with coverage presented for both Day 1 and...
Eshal Associates vs Ahmed Glass in President Trophy Grade-II (Gold)
Pakistan Cricket is broadcasting a live match between Eshal Associate and Ahmed Glass as part of the President Trophy Gr...