I Gave Claude Code a Telegram Number

A 300-line Python bot on the Proxmox host turns my terminal coding agent into something I can text from anywhere — streaming its tool calls into the chat while it works on the lab.

The Idea

Claude Code lives in my terminal. My terminal lives on machines. And I'm a dad now — which means I essentially never get to sit at one. My computing time collapsed, overnight, to whatever can happen on a phone, one-handed, while a baby sleeps on the other arm.

The lab, meanwhile, doesn't care about any of that. Things still need restarting, checking, tweaking at inconvenient times — and the post about retiring the baby tracker already covered what happens to any workflow that assumes I have free hands and attention. So I built the obvious bridge: a Telegram bot that runs Claude Code headless on the Proxmox host. Text it a task; the agent does the work; the chat shows everything it did.

How It Works

The whole thing is a single Python file running under systemd on pve1 — python-telegram-bot on long polling, no webhook, no exposed port:

  • Each message spawns Claude Code as a subprocess: claude -p --output-format stream-json. Headless mode with streaming JSON output is the entire integration surface — the bot is fundamentally a renderer for that stream.
  • Tool calls stream into the chat live. This turned out to be the most important design choice. You watch the agent SSH into a container, read a log, edit a config — each step appearing as it happens. An agent you can observe mid-flight is an agent you can interrupt before step three becomes regrettable.
  • Sessions persist. Each chat holds a session ID with a TTL (an hour by default), so "now do the same for the other container" works — follow-up messages resume the same context. /new starts fresh, /model switches between model tiers per user, /cancel kills a run mid-flight.
  • Attachments work. Photos and documents download to a temp path that gets passed to Claude — so "here's a screenshot of the error" is a valid bug report to your infrastructure.
  • Secrets come from my secrets manager at launch; state (sessions, per-user model prefs) survives restarts in a little JSON file.

The Security Posture

Let's be honest about what this is: a remote-code-execution service for my network, with a chat UI. It's designed accordingly:

  • A hard allowlist of exactly one user. Not a password, not a group policy — the bot ignores every other human on Telegram at the API-handler level.
  • Long polling only — the bot dials out to Telegram; nothing on my network listens for anyone.
  • It runs where I can watch it: journald logs, a systemd unit I can stop from anywhere I have Tailscale.

Would I recommend this pattern to an enterprise? Absolutely not. For a home lab with one authorized human? The threat model is "me, but lazier."

What It's Actually Like

The surprise wasn't that it works — headless Claude plus a chat frontend is not much code. The surprise was how much of my lab administration moved to it. "Check why uptime-kuma is alerting." "Pull the latest on the docs container and rebuild." "What's eating disk on pve1?" Each of these used to mean later, when I'm at a computer — and "later," for a new parent, is a euphemism for never. Now they mean thirty seconds of thumb-typing, then watching the tool calls scroll by like a tiny movie about my own infrastructure, narrated to a sleeping baby.

Fatherhood didn't end my lab time. It just moved it to my thumbs.