2026-05-28 Hermes Agent Telegram setup on a leased Mac mini M4 (HK / JP / KR / SG / US)
You can run Hermes Agent as a Telegram bot on the same Mac mini M4 host that already builds iOS apps over SSH. The gateway process (hermes gateway) holds your BotFather token, enforces an allowed-user allowlist, and forwards chat messages into Hermes's tool loop—so you can approve CI fixes or ask architecture questions from a phone without keeping a laptop awake.
For serverless Modal/Daytona terminal backends and near-idle compute, see our Hermes serverless Modal/Daytona hibernate guide.
Why Telegram on a builder Mac
Telegram fits headless leases because the gateway uses outbound long polling by default: your Mac initiates HTTPS to Telegram's API, so you do not need to expose inbound ports on a shared builder (contrast with webhook mode on public HTTPS endpoints).
Typical reasons teams wire Hermes to Telegram on a lease host:
- On-call triage when GitHub Actions or
xcodebuildfails overnight—reply from mobile while the agent reads logs on NVMe. - Separation of concerns—keep OpenClaw for repo automation and Hermes for personal/ops chat, but only if you accept two gateway processes (RAM and token isolation).
- Migration path from OpenClaw messaging—Hermes documents
hermes claw migratein upstream README; see our Hermes vs OpenClaw vs OpenHuman matrix before running both gateways on one 16 GB host.
Apple Mac mini specifications list 16–24 GB unified memory. Budget ~1–2 GB for hermes gateway plus your model provider's working set before stacking Xcode parallel testing.
How the Telegram gateway fits together
Hermes separates CLI chat (hermes TUI) from messaging (hermes gateway). For Telegram, configuration lands in ~/.hermes/.env (or wizard output), logs under ~/.hermes/logs/gateway.log, and optional launchd service on macOS.
| Component | Path / command | Role |
|---|---|---|
| Install | curl -fsSL …/install.sh | bash | Python 3.11 + hermes CLI |
| Model/auth | hermes setup | Provider API keys (BYO or Nous Portal) |
| Telegram config | hermes gateway setup | Bot token + TELEGRAM_ALLOWED_USERS |
| Foreground test | hermes gateway | Long-polling bot until Ctrl+C |
| Service | hermes gateway install → hermes gateway start | launchd on macOS, survives logout |
| Health | hermes doctor, hermes gateway status | Config + process checks |
Data flow (polling mode):
- You message the bot in Telegram.
- Gateway receives update via Telegram Bot API (outbound poll).
- Hermes agent loop runs tools on configured terminal backend (local, Docker, or SSH to same host).
- Reply text (and optional
MEDIA:/pathattachments) returns through the gateway to Telegram.
Security defaults that matter: upstream uses numeric user IDs in TELEGRAM_ALLOWED_USERS, not @usernames. Anyone with your bot token can impersonate the bot—treat tokens like API keys and revoke via BotFather /revoke on leak.
Official reference: Hermes Telegram docs (BotFather, privacy mode, webhooks).
BotFather and access control
Create the bot
- Open @BotFather →
/newbot. - Set display name (e.g.
Builder Hermes) and username ending inbot. - Copy the token format
123456789:ABCdefGHIjklMNOpqrs…— store in a password manager, not git.
Allowlist your user ID
Message @userinfobot for your numeric ID (e.g. 123456789). Hermes rejects strangers when TELEGRAM_ALLOWED_USERS is set.
Optional hardening on shared leases:
- Single-user allowlist only—no wildcard "team" IDs until you understand tool scope.
- Separate bot per environment (staging vs production lease) so a leaked staging token cannot touch production workspaces.
- Disable group access until DM behavior is verified.
Configure Hermes for Telegram
Interactive (recommended)
hermes setup # model provider first, if fresh install
hermes gateway setup # pick Telegram → paste token → allowed user IDs
Manual ~/.hermes/.env
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrSTUvwxYZ
TELEGRAM_ALLOWED_USERS=123456789
# Optional team: TELEGRAM_ALLOWED_USERS=111111111,222222222
Verify files are mode 600 and excluded from dotfile sync you do not trust.
Foreground smoke test:
hermes gateway
Send hello in Telegram; expect a reply within seconds. Stop with Ctrl+C before installing launchd.
Eight-step headless rollout on Mac mini M4
- Install Hermes on the lease user account (not root): official install.sh from NousResearch/hermes-agent.
- Run
hermes setup— configure model provider; confirmhermes doctoris clean. - Create BotFather token and numeric user ID (sections above).
- Run
hermes gateway setup— select Telegram; confirm~/.hermes/.envcontains token + allowlist. - Foreground test —
hermes gateway, message bot, trigger a harmless tool (e.g.pwdvia agent); Ctrl+C stop. - Install service —
hermes gateway installthenhermes gateway start(macOS launchd). - Persist env for daemons — ensure API keys in
~/.hermes/.envload for launchd (same user as install); compare with OpenClaw launchd PATH notes if tools fail only in background. - Monitor —
hermes gateway status,tail -f ~/.hermes/logs/gateway.log, and Telegram/newto reset poisoned sessions after config changes.
Quotable operational rule: One gateway process can serve multiple platforms (Telegram, Discord, Slack, etc.)—you do not need separate daemons per chat app.
launchd and SSH-only leases
On macOS leases, hermes gateway install registers a user launchd job—analogous to OpenClaw's openclaw onboard --install-daemon pattern documented on this blog.
hermes gateway install
hermes gateway start
hermes gateway status
tail -f ~/.hermes/logs/gateway.log
After SSH disconnect, the gateway should stay up under the same Unix user that ran install. If the job stops, check:
launchctl list | grep -i hermes- log file for
TELEGRAM_BOT_TOKEN/ auth errors - whether a lease policy kills long-running user agents at logout (rare on dedicated M4; common on shared shells)
Docker backend note: if terminal.backend: docker, files sent via MEDIA:/path must exist on the host path the gateway reads—not only inside the container. Mount host-visible volumes per upstream telegram.md.
Group chats (optional)
Telegram privacy mode defaults ON—bots in groups only see /commands, replies to the bot, or admin-visible traffic. For broader group context, disable privacy in BotFather (Bot Settings → Group Privacy → Turn off) and remove/re-add the bot to each group so Telegram refreshes state—or promote the bot to group admin.
For "observe but don't reply until @mention" behavior, upstream documents TELEGRAM_OBSERVE_UNMENTIONED_GROUP_MESSAGES and allowlisted chat IDs—use only after reading the telegram.md group section.
Troubleshooting
Symptom: bot never replies (polling silent)
| Check | Command / fix |
|---|---|
| Gateway running? | hermes gateway status; restart hermes gateway start |
| Token valid? | Re-paste from BotFather; /revoke old token if leaked |
| User not allowlisted? | Add your numeric ID to TELEGRAM_ALLOWED_USERS, restart gateway |
| Model/auth failure? | hermes doctor; test hermes CLI locally first |
| Logs | grep -i error ~/.hermes/logs/gateway.log | tail -20 |
Symptom: 401 Unauthorized or Conflict: terminated by other getUpdates
- 401: wrong or revoked
TELEGRAM_BOT_TOKEN. - Conflict: two processes polling the same bot—stop duplicate
hermes gatewayforeground sessions or a second host using the same token.
hermes gateway stop
pkill -f "hermes gateway" 2>/dev/null || true
hermes gateway start
Symptom: works in foreground, fails under launchd
- launchd runs with a minimal environment—confirm
~/.hermes/.envis readable and provider keys are present. - Compare
echo $PATHin SSH vslaunchctl print gui/$(id -u)environment (macOS); align tool paths like Node if plugins shell out.
Symptom: group sees nothing except /commands
- BotFather privacy mode still ON—disable or make bot admin; re-add bot to group.
FAQ
hermes claw migrate, but running both bots on one host duplicates RAM and risks conflicting automation. Pick one messaging owner per lease.memory_pressure during archive + gateway peak; pause gateway during large xcodebuild matrices if needed.Headless M4 for Hermes Telegram gateway
SSH-first Apple Silicon in HK, JP, KR, SG, and US—enough unified memory for a 24/7 Telegram gateway beside Xcode lanes when you need always-on ops chat.