This 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 laterloggingorplugins.loadchange, 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 toallowlist(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 justAGENTS.mdto 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
statusin 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 inallowFrom, andrequireMention: false; you're no longer inopenmode 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 600it, 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.mdalready handlesstatus/send/restart/option replies prompt-driven; the router just makes it faster and stricter) - The
opsagent (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.
Top comments (0)