Control OpenClaw without touching a keyboard—from your pocket. Say something into your phone; seconds later it’s a timestamped line in a file on a server. Convert your software ideas into action with only your voice.
Why this is powerful: Ideas hit you when you’re walking, driving, or half-asleep. Usually they’re lost. Here, your voice goes straight into OpenClaw’s workspace. No opening an app, no typing, no “I’ll log it later.” The agent’s context is always up to date. You’re not just logging—you’re feeding the same system that can later act on those ideas. Voice is the lowest-friction input you have; this makes it first-class.
Note: A raw agents file that distills this post and the project is here: hey-siri-openclaw-agents.md
OpenClaw on a Hetzner server, two Telegram bots, and an Apple Shortcut. You run the Shortcut My Idea Is with your voice (or type “log: …” in Telegram); the bot appends a line to idea-log.md. Two days to build. This post is the runbook—phases, scripts, and what broke.
Two days. Two bots. Zero taps.
March 7–8, 2026. “Hey Siri, my idea is …” → Shortcut sends to a private Telegram channel → OpenClaw on the server sees it and appends to the log. Or open Telegram and DM “log: my idea” to the bot. Same result.
Stack:
- Hetzner Cloud in a new project (its own API token and server)
- Terraform for the server, SSH key, persistent volume, and cloud-init (Tailscale, UFW, Fail2ban, Node 22, OpenClaw, systemd gateway)
- OpenClaw with profile
main, bound to 127.0.0.1 on a custom port so the gateway is not on the public internet - Two Telegram bots: LOGGER (the OpenClaw bot you talk to in DMs) and MESSENGER (used only by the Shortcut to post into a private channel)
- Private Telegram channel where only the two bots are members; the Shortcut posts there with the MESSENGER token; the LOGGER receives those posts as
channel_postand logs them - Apple Shortcut dictates, URL-encodes “log: <text>”, and hits the Telegram Bot API. That’s it.
Same idea as my Cursor + Rails post: every phase has a script. You don’t mark a step done until the script passes.
Architecture (high level):
Why I didn’t run it on my Mac (and why two bots)
Dedicated server: I had planned to run OpenClaw on my Mac under a locked-down user profile (I would never run it as root). A friend pushed back on running the agent on my Mac at all. He was right about blast radius: if something goes wrong, my main machine and data are not on the same host. So OpenClaw runs only on the Hetzner server; my Mac is SSH and this repo; the iPhone is Telegram and the Shortcut.
Two bots because Telegram won’t play ball. The Shortcut can’t send as me without MTProto (user login). It can only call the Bot API. So if the Shortcut uses the LOGGER token and sends a message, that message is from the bot. Telegram does not deliver “messages the bot sent” to the bot. The LOGGER would never see it. Fix: a second bot (MESSENGER). Shortcut uses MESSENGER’s token, sends to a channel. LOGGER is a channel admin, so it gets channel_post. Same log, zero taps. I tried MESSENGER to LOGGER in a DM first. Telegram: “Bad Request: chat not found.” Channel required.
Validation or it didn’t happen. I never marked a step done until a script passed. ./scripts/validate/run-all.sh runs phases 0.1 through 6 and stops at first failure. For full reproducibility: recreate-and-validate.sh destroys the server (volume survives), apply again, cloud-init, run-all. Terraform plus push scripts get you back to green.
Message flow (two paths into the same log):
Validation loop:
A server that only answers to Tailscale and SSH
New Hetzner project. Token in .env as HCLOUD_TOKEN. Terraform creates the server, your SSH key, a persistent volume. Cloud-init at first boot: non-root user, Tailscale (if you set the auth key), UFW (SSH and Tailscale only), Fail2ban (3 tries then 24h), Hetzner backups. The box never exposes the OpenClaw gateway to the public internet.
Prove it:
cd terraform && terraform init && terraform apply
ssh root@$(cd terraform && terraform output -raw server_ip)
ssh openclaw@$(cd terraform && terraform output -raw server_ip) 'tailscale status'
ssh openclaw@$(cd terraform && terraform output -raw server_ip) 'ufw status'
ssh openclaw@$(cd terraform && terraform output -raw server_ip) 'fail2ban-client status sshd'
./scripts/validate/run-all.sh
Phase 0.6 checks backups via the Hetzner API. Needs HCLOUD_TOKEN in the environment.
OpenClaw on the box (never as root)
Cloud-init already created the profile dir, a minimal config, Node 22, and OpenClaw. Plus a systemd unit so the gateway starts at boot. One script from the Mac pushes the Anthropic API key to the server; Terraform never touches secrets.
Prove it:
./scripts/phase-1.3-configure-anthropic.sh
ssh openclaw@$(cd terraform && terraform output -raw server_ip) 'node -v && openclaw --profile main --version'
ssh openclaw@$(cd terraform && terraform output -raw server_ip) 'cat ~/.openclaw-main/openclaw.json | head -20'
./scripts/validate/phase-1.sh
The gateway runs with --allow-unconfigured so it comes up before Telegram is configured. Once Phase 2 is done, a test message from Telegram gets a reply.
Telegram: two bots and a channel (because one bot can’t see its own messages)
LOGGER is the bot you chat with. Create it with @BotFather, put the token in .env as TELEGRAM_LOGGER_BOT_TOKEN, run the push script so the server has it as TELEGRAM_BOT_TOKEN. MESSENGER is the second bot; same drill, add TELEGRAM_MESSENGER_BOT_TOKEN and TELEGRAM_MESSENGER_BOT_ID to .env. Don’t push MESSENGER to the server; the Shortcut and validation scripts use it from your Mac. Get your numeric user ID (@userinfobot), set TELEGRAM_HUMAN_USER_ID. The LOGGER allowlist must include both your ID and MESSENGER’s ID.
Channel: Private channel. Add both bots as admins. Not you. Get the channel ID (negative number), set TELEGRAM_PASSIVE_CHANNEL_ID. Shortcut uses MESSENGER token and that channel ID to post “log: <idea>”.
Prove it:
./scripts/phase-2.1-create-telegram-bot.sh
curl -s "https://api.telegram.org/bot${TELEGRAM_LOGGER_BOT_TOKEN}/getMe" | jq -r '.result.id'
curl -s "https://api.telegram.org/bot${TELEGRAM_MESSENGER_BOT_TOKEN}/getMe" | jq -r '.result.id'
./scripts/phase-2.2-configure-telegram-channel.sh
./scripts/validate/phase-2.sh
curl -X POST "https://api.telegram.org/bot${TELEGRAM_MESSENGER_BOT_TOKEN}/sendMessage" -d "chat_id=${TELEGRAM_PASSIVE_CHANNEL_ID}" -d "text=log%3A%20channel%20test"
ssh openclaw@$(cd terraform && terraform output -raw server_ip) 'cat /mnt/openclaw-data/workspace/notes/idea-log.md'
If you use the default profile path instead of the volume, replace /mnt/openclaw-data/workspace/notes/idea-log.md with ~/.openclaw-main/workspace/notes/idea-log.md.
The bug that made the agent rewrite the whole file
The workspace has notes/idea-log.md. I deploy AGENTS.md so that when the agent sees “log” or “log: …”, it appends one line. I hit a bug: the agent tried a small in-place edit and failed. Fix: tell the agent to read the full file and write it back with the new line appended. Re-run the Phase 3.2 script to push the fix.
Prove it:
./scripts/phase-3.1-ensure-notes-workspace.sh
./scripts/phase-3.2-configure-notes-log-behavior.sh
./scripts/validate/phase-3.sh
Claude on the server
I used Anthropic from the start (paid). Phase 1.3 pushes the API key to the server and sets the model. Phase 4 validation: config references Anthropic, key exists on server, no key in the repo.
Prove it:
./scripts/validate/phase-4.sh
Hey Siri, my idea is … Then nothing.
Manual path: Open Telegram, DM the LOGGER bot, type “log: my idea”, Send. Passive path: Shortcut dictates, URL-encodes “log: <LogMessage>”, builds the Telegram Bot API URL with MESSENGER token and channel ID, Get Contents of URL. No opening Telegram. No tap. LOGGER receives channel_post, appends to idea-log.
The Shortcut in the screenshot isn’t optimized; it may have redundant encode or variable steps. What matters: the request hits the Bot API with MESSENGER token and channel ID, and the LOGGER gets the channel_post and appends.
Important: URL-encode the message body or spaces and special characters will break the request.
Screenshot (tokens and channel ID redacted):

Validate Phase 5: Documented flow works from the iPhone. If TELEGRAM_API_ID and TELEGRAM_API_HASH are set, phase-5.sh sends “log: Phase 5.1 validation” as your user and verifies idea-log. Run the Shortcut once and confirm the new line on the server.
ssh openclaw@$(cd terraform && terraform output -raw server_ip) 'cat /mnt/openclaw-data/workspace/notes/idea-log.md'
./scripts/validate/phase-5.sh
What I didn’t build (yet)
Phase 6: audit prompt and alerts for non-allowlisted users. Placeholders only. Hardening (Tailscale, Fail2ban, UFW, allowlist, DMs only) is done in Phase 0 and 2.
When Telegram said “chat not found”
Bot-to-bot DM: Sending from MESSENGER to LOGGER in a DM returns “Bad Request: chat not found”. Telegram’s docs say bots do not receive messages from other bots in DMs. So I switched to the private channel and made the LOGGER a channel admin so it receives channel_post.
Allowlist and channel_post: OpenClaw’s allowlist for Telegram is usually numeric user IDs. For channel posts, the sender can be the channel (negative ID). I had to ensure the config (or a small patch) allows the passive channel so that channel_post from that channel is accepted. Check the OpenClaw version and docs for channel_post and allowlist; I used a recent build that treats channel_post like a message and runs it through the same pipeline.
Append to idea-log: The agent failed with “Edit in …/idea-log.md (42 chars) failed” when it tried an in-place edit. I updated the AGENTS.md instructions so the agent reads the full file and writes it back with one new line appended. Re-run the Phase 3.2 script to push the fix.
Recreate and secrets: After terraform destroy (with targeted destroy so the volume remains), terraform apply gives you a new server and cloud-init runs again. You must re-push the LOGGER token (./scripts/phase-2.1-create-telegram-bot.sh), re-run the Phase 2.2 config so allowlist has both your user ID and MESSENGER bot ID, and restart the gateway so it picks up Telegram and model config. The recreate-and-validate.sh script does that when the relevant env vars are set.
Shortcut URL encoding: URL-encode the full “log: <text>” or the request breaks.
What actually worked
Validation at every step. Terraform plus cloud-init so one apply gives you the server. Two bots and a channel once I accepted that bot-to-bot DMs are a no. Persistent volume so I can destroy and recreate the server without losing the log. One script, run-all.sh, that runs every phase and stops at the first failure.
Why this is powerful (again): Siri becomes a direct line into your AI agent. You don’t “remember to write it down”—you say it, and it’s already in the workspace OpenClaw reads. That means more ideas captured, less friction, and a single place (the log) that both you and the agent share. The power isn’t the plumbing; it’s that voice finally has a first-class path into the same system that can execute.
If you steal this idea
Secrets in .env, never in the repo. LOGGER token gets pushed to the server; MESSENGER stays on your Mac and in the Shortcut. Reusable Tailscale auth key so a new server can rejoin. URL-encode “log: <text>” in the Shortcut. Run run-all.sh after each change; run recreate-and-validate.sh (with source .env) to prove destroy and apply get you back to green. To read the log from your Mac: ssh openclaw@<server> 'cat /mnt/openclaw-data/workspace/notes/idea-log.md'.
The runbook and validation scripts live in the openclaw-setup repo. The repo is private for security. For more info or access: joseph.e.combs@gmail.com.