Zero-Latency, Touch-First Mobile ChatOps Gateway for Herdr & AI Coding Agents
herdr-chatops is a dual-track remote-control layer for Herdr multiplexer sessions and modern AI coding agents (OMP, Claude Code, Codex). It gives you deep terminal immersion over WireGuard/Tailscale SSH and touch-first asynchronous control over Telegram — approvals, in-place log refreshing, and clean session JSONL deliveries, all from your phone, with zero typing required.
- 🚀 Dual-Track Architecture — Sub-5ms WireGuard/Tailscale SSH for deep immersive work, paired with a Touch-First Telegram ChatOps surface for asynchronous approvals & artifact delivery. Pick the right track for the moment; never compromise on either.
- 📱 Touch-First Mobile Ergonomics — A persistent 6-button reply keyboard: Status, Logs, Approve (
y), Reject (n), Abort (C-c), Switch Pane. No on-screen typing on mobile — every control is one tap. - 🔄 In-Place Live Refresh — Telegram cards edit themselves in-place with idempotent deduplication. No stream of duplicate messages, no chat clutter.
- 🤖 Session-Aware Deliverables — Extracts clean assistant responses directly from session JSONL (OMP / Claude Code), bypassing TUI statusline noise, "PUA" glyphs, and ANSI escape codes.
- 🔒 Zero-Leak Security — Secrets resolve from environment variables,
~/.config/herdr-chatops/config.json, and macOS Keychain (securitywith-s tg-bots -a <account> -wfor unattended reads) — in that order. No plaintext tokens ship in your repo.
flowchart TD
Phone["📱 Mobile Telegram"] -->|"HTTPS (long-poll)"| Cloud["☁️ Telegram Cloud"]
Cloud -->|"Update (tap y / n / logs)"| Daemon["🐰 herdr-chatops daemon (Bun)"]
Daemon -->|"read secret"| Keychain["🔐 macOS Keychain / XDG config"]
Daemon -->|"control + read panes"| Herdr["🧭 Herdr multiplexer"]
Daemon -->|"stream session JSONL"| OMP["🤖 OMP / Claude Code session JSONL"]
Herdr -->|"render TUI"| Ghostty["🖥️ Ghostty terminal"]
Daemon -->|"in-place edit card"| Cloud
Cloud -->|"live card refresh"| Phone
The daemon runs locally on your macOS/Linux host. It speaks to Telegram over HTTPS, reads secrets from the Keychain, drives the Herdr multiplexer, and tails OMP/Claude Code session JSONL files to surface clean assistant output back into the Telegram card — all in-place.
- Bun ≥ 1.1 (runtime + test runner); Node.js ≥ 20 also works
- Herdr ≥ 0.8.0 installed and on
PATH(or setHERDR_BIN) - A Telegram Bot Token from @BotFather
- Your Telegram Chat ID (user or group) — use @userinfobot to discover it
git clone https://github.com/steven/herdr-chatops.git
cd herdr-chatops
bun installRun the interactive wizard (writes ~/.config/herdr-chatops/config.json):
bun run herdr-chatops initOr configure with a .env file (see .env.example):
HERDR_TELEGRAM_TOKEN=<your-bot-token>
HERDR_CHAT_ID=<your-chat-id>
HERDR_BIN="$(which herdr)"🔒 Never commit real tokens or chat IDs.
.env,config.json, and*.offsetare git-ignored. See Zero-Leak Security.
macOS (Launchd) — copy the bundled template, substituting variables, then load it:
mkdir -p ~/Library/LaunchAgents
sed -e "s|{{BUN_PATH}}|$(which bun)|g" \
-e "s|{{PROJECT_DIR}}|$(pwd)|g" \
-e "s|{{HOME}}|$HOME|g" \
templates/com.herdr-chatops.plist > ~/Library/LaunchAgents/com.herdr-chatops.plist
launchctl load ~/Library/LaunchAgents/com.herdr-chatops.plist
launchctl start com.herdr-chatops
# Logs: ~/Library/Logs/herdr-chatops.logLinux (Systemd) — user service:
mkdir -p ~/.config/systemd/user
sed -e "s|{{BUN_PATH}}|$(which bun)|g" \
-e "s|{{PROJECT_DIR}}|$(pwd)|g" \
-e "s|{{HOME}}|$HOME|g" \
templates/herdr-chatops.service > ~/.config/systemd/user/herdr-chatops.service
systemctl --user daemon-reload
systemctl --user enable --now herdr-chatops
# Logs: journalctl --user -u herdr-chatops -fForeground (dev)
bun run dev # bun --watch + start| Command | Description |
|---|---|
herdr-chatops start |
Run the daemon (default; long-poll Telegram + Herdr bridge) |
herdr-chatops status |
Print config + test Herdr/Telegram connectivity |
herdr-chatops init |
Interactive configuration wizard (writes config.json) |
herdr-chatops help |
Show usage |
| Button | Action |
|---|---|
| Status | Snapshot daemon + active pane state |
| Logs | Pull the latest session JSONL tail, cleaned |
Approve (y) |
Send y to the active pane (confirm a prompt) |
Reject (n) |
Send n to the active pane (decline a prompt) |
Abort (C-c) |
Send Ctrl-C to abort the running task |
| Switch Pane | Cycle the active Herdr pane |
When an agent is blocked, an expandable approval card pushes one-tap ✅ Approve (y) / ❌ Reject (n) inline buttons. All status/log cards refresh in-place — the previous message is edited, not replaced.
Secrets are resolved in this strict order (first hit wins):
-
Environment variables —
HERDR_TELEGRAM_TOKEN,HERDR_CHAT_ID, … -
XDG config —
~/.config/herdr-chatops/config.json -
macOS Keychain — read unattended on Darwin via:
security find-generic-password -s tg-bots -a "$HERDR_KEYCHAIN_ACCOUNT" -w(the daemon also accepts the
-T /usr/bin/securityvariant for unattended access grants).
No token is ever written to the repository. .env, config.json, and *.offset tracking files are git-ignored by default.
bun test # unit tests
bun run typecheck # tsc --noEmitReleased under the MIT License. Copyright (c) 2026 Steven.