Round one · spoken, then shipped
Spoken at 16:27. The proof came back at 16:43.
Watch
STAY: your terminal in your pocket. FETCH: hand it the long job. OFF-LEASH: steer it, scale it, never wait.
01 FETCH
Away from your desk. The deploy is half finished, or you just thought of something. You open Telegram and type it. Claude Code runs it on your machine, in your repos, with your config and your sessions.
The progress block is one Telegram message, edited as the run goes. Subagent activity streams in too, indented.
validateCart() returns early when items is empty, so the discount branch never runs. Fixed and re-ran, all 14 pass now.02 THE KIT
A service under launchd or systemd long polls the Telegram Bot API. Outbound HTTPS only. No tunnel, no webhook, no inbound port, no relay.
Long jobs run in their own Claude session. If a worker is busy another spawns, so jobs run beside each other instead of queueing behind each other.
Message while a task is running and it goes into the running task, the same as typing mid turn in the CLI. Change the instruction without killing the work.
Remind me at 8, or every morning summarize yesterday’s commits. The second one actually runs, as a real Claude task, and reports back.
OpenAI Codex is billed separately, so an Anthropic limit does not touch it. Ask it directly, hand it a whole job, or let it pick up queued work while Claude is walled.
Hold a personal and a work subscription. See each one’s live headroom, swap with one tap, and when the active account is rate limited, background work moves to one that is not.
Send a screenshot with “why does this look broken?” Images, PDFs and code up to 20 MB land in inbox/ and the caption is the instruction. Voice notes get transcribed and run.
Ask today, follow up tomorrow, same conversation. Sessions are per lane and per directory, survive restarts and reboots, and your custom slash commands work.
Also in the box: named, resumable chats live progress model switching usage and context remote restart log tail self healing watchdog
03 STAY
Around two thousand lines of Node that long poll the Telegram Bot API and pipe messages into claude -p. The only network traffic is your machine calling Telegram.
You type a message, send a screenshot, or hold the mic. Normal Telegram, nothing installed.
Your machine long polls it over outbound HTTPS. Telegram never calls in. Every update is checked against your chat ID before anything else happens, so strangers who find your bot get silence.
It runs Claude Code and Codex locally, in your repos. Two lanes: a chat lane that is always answerable, and a worker pool that takes the long jobs.
chat lane
One task at a time. Messages sent mid task steer into it, so it is always reachable.
worker pool
bg1, bg2, bg3 and on. Parallel, and never refused: up to three Claude workers run at once and the rest start as slots free. Each worker is a fresh, self contained session, discarded when it drains.
the handback
A finished job reports to the chat session, not to you, framed as untrusted worker data. The assistant decides whether more work is needed, then sends you a short human update. You get a colleague, not a log stream.
No tunnel. No webhook. No cloud relay.
Chat IDs come from Telegram’s servers and cannot be spoofed by a sender.
The bot token lives only in config.json, chmod 600 and gitignored.
Nothing leaves your machine except the Telegram messages themselves, voice note audio if you turn on transcription, and calls to Anthropic’s own OAuth endpoints if you enroll more than one account.
The default permission mode is skip permissions, because a headless run cannot answer a prompt. /yolo off switches to accept edits. Know which one you are running.
It also gives a Telegram chat the ability to run code on your machine. Anyone with access to your logged in Telegram can run commands. Turn on two step verification and read the security section before you install.
04 OFF-LEASH
A headless run occupies its process until it finishes, so a twenty minute job would normally mean twenty minutes of silence. Long commands, anything you prefix bg:, scheduled tasks and handoffs from a session go to their own worker instead.
The pool never refuses a job. Up to three Claude workers run at once (maxConcurrentWorkers in config.json); a job past that waits in the drop box in arrival order, shows in bg.mjs ps, and starts the moment a worker exits. Codex jobs are not counted. Workers are detached, in their own process group, writing to a file rather than a pipe, so restarting the daemon does not kill them. On the next boot it re-attaches to the ones still running and tells you about the ones that died.
node bg.mjs --file ./brief.md
Pass a real brief as a file, never as a shell argument: backticks inside it would turn into command substitution and the worker would get a brief with those terms silently blanked out.
05 THE TUG
A running job used to be unreachable. The only way to change its instructions was to kill it and re-dispatch, which throws away all the context it had already built.
Now a message sent mid task is steered into the running run over the CLI’s streaming input mode. It either folds your note into the current turn or answers it right after. Only when steering is impossible, because nothing is running yet or the run is already finishing, does the message queue instead.
/steer latest skip the browser step, the harness is down
Steer when the brief is still valid and you are adding or correcting an instruction. Kill and re-dispatch only when the brief itself was wrong.
06 THE ROUNDS
Not a feature list. This is one month of one person’s real traffic through the bridge, 2026-08-09 to 2026-09-08, counted off the machine that ran it. The four exchanges below actually happened. They are paraphrased and every name in them has been taken out.
1,973
prompts from the phone
217
spoken, not typed
228
screenshots and photos in
614
jobs handed to a worker
Round one · spoken, then shipped
Spoken at 16:27. The proof came back at 16:43.
Round two · a screenshot is the bug report
Nine minutes from the photo to a committed fix with the tests green.
Round three · correct it, do not kill it
Two corrections into a job that was already running, about ninety seconds apart. Typed plainly and relayed in; /steer aims one at a named worker directly.
Round four · nobody asked for this one
It fires at 08:30 every morning, into the same chat, asked or not.
One day, in order · 2026-09-04
Twelve moments from a day that carried eighty three messages. Local time, one person, one phone.
Counted off the bridge’s own files, not from memory: the transcript each lane writes, the inbox every attachment lands in, and the live schedule. The four replays and the day above come out of those same transcripts.
07 SETUP
Requirements: Claude Code, Node 18 or newer, macOS with launchd or Linux with systemd, and a Telegram account.
git clone https://github.com/zalogarcia/leash.git ~/leash
cd ~/leash
./install.sh
It checks prerequisites, walks you through creating a bot with BotFather, auto detects your chat ID, writes a config.json at chmod 600, installs the service and starts it.
./install.sh --dry-run
Preview exactly what it would do without touching anything.
/help
That is the whole install. Everything after this is optional.
Export an OPENAI_API_KEY in your shell profile, which the bridge reads because service managers do not. Without a key, voice notes are handed to Claude as audio files instead.
Copy the block in templates/CLAUDE.md.example into your own CLAUDE.md. That is what teaches your sessions to hand long jobs to the background lane and to manage your reminders in plain English.
08 QUESTIONS
Nowhere it would not already go when you run Claude Code in your terminal. The bridge adds no destination of its own: your prompts and its replies travel as Telegram messages, voice note audio goes to transcription if you turn that on, and enrolling a second account adds calls to Anthropic’s own OAuth endpoints for the usage numbers. Your repos stay where they are.
None of the three. The bridge long polls the Telegram Bot API over outbound HTTPS, so nothing inbound ever reaches your machine. No webhook, no third party relay. Your machine does need to be awake: messages sent while it sleeps queue at Telegram and are skipped as stale after an hour.
They get silence. Every incoming update is checked against your chat ID before anything else happens, so there is no reply, no download and no execution for anyone else. Chat IDs come from Telegram’s servers and cannot be spoofed by a sender. The real key is your Telegram account itself, so turn on two step verification and keep an eye on your device list.
Yes, with systemd. It is tested day to day on macOS with Claude Code 2.x. The Linux path is included and structurally sound but less battle tested, so issues and pull requests are welcome.
The bridge is MIT licensed and free. You pay for what it drives: your own Claude Code subscription, plus OpenAI usage if you turn on voice note transcription or use Codex as the second engine.
Enroll both. You get each account’s live headroom, one tap swaps, and when the active account is rate limited the bridge rotates new background work to an enrolled account that still has room. Workers already running are never killed by a swap. When every account is walled, newly queued jobs run on Codex instead of waiting, and the reply says so. This is a switcher between subscriptions you already pay for, not a way around anyone’s plan limits.
Because it sits at your feet all day, fetches on command, and looks deeply unimpressed while doing it.