This was the third time in a week I'd said the same thing.
Claude Code had just diagnosed a 500 error on velvet-reply-prod. It went straight for SSH, pulled the relevant file, and came back with a clean answer: config/credentials.yml.enc contained the string BROKEN_AGAIN — 13 bytes, where there should have been 724 bytes of encrypted Rails credentials. The Puma process couldn't boot. Mystery solved in about 90 seconds.
I typed: "No SSH. Use the bot. Like a real user."
The reflex toward direct access
When you give an AI agent access to a server, it will use that access. That's not a flaw — it's doing exactly what you set it up to do. SSH gives you everything: raw file contents, process state, logs without formatting, the ability to run anything. From a diagnostic standpoint, it's the highest-bandwidth path to the truth.
The problem is that's not the path your users have.
I'd already learned this lesson once, expensively. I wrote about it in another post: I put an autonomous agent on my server with full access and no confirmation step, and it quietly removed a feature it decided was dead code. When an AI has direct access and no guardrails, it fills in context gaps with its own judgment. Sometimes that judgment is wrong. On production, wrong is expensive.
So mttrly was built around a different assumption: the agent monitors, diagnoses, and proposes — but it acts through the same interface a human uses. Telegram message, confirmation prompt, then execution. Every destructive command requires approval. This isn't just a UX decision. It's the architecture.
But I kept giving Claude Code SSH credentials anyway, for development work. And it kept using them when I asked it to investigate production problems. Fast and correct — and completely beside the point.
What SSH found vs what the bot found
After the third redirect, I watched what happened when Claude Code actually went through the bot.
The SSH path had given me a file with the wrong contents. Clear, actionable, done. The bot gave me something different.
First, the monitoring trail. Before Claude Code even got to the credentials question, the bot surfaced this:
🚨 ALERT
Server: velvet-reply-prod
Incident: Service velvetposter is not running.
⚠️ This is the 91st service down incident in 7 days.
💡 Recommendation: Check service logs for root cause —
recurring crashes may indicate a bug.
Then immediately:
🛠 Auto-remediation 1/2: restarting velvetposter on velvet-reply-prod
✅ Incident resolved
Downtime: 1s
Ninety-one incidents. Seven days. The service had been crashing and auto-restarting on a loop for a week. Every crash lasted about one second because the auto-remediation caught it. The 500 errors were real — anyone who hit the service in that one-second window got one — but brief enough that nobody had complained yet. From the outside, the server looked fine. Just a process quietly dying and coming back, thirteen times a day.
SSH had found the cause. The bot found the story.
Those are different things, and for a while I confused them. Cause is why something broke. Story is how long it's been broken, how often, what the system has been doing about it, and what it looks like to someone who depends on it. A root cause without context is an answer to a question nobody asked yet.
The harness doesn't care who you are
There's something else that happened during these sessions that I didn't fully notice until I looked back at the logs.
Every time Claude Code wanted to run a command through the bot — check a log file, read the environment config, query the database — the bot responded the same way it responds to me:
About to: Command Execution on "velvet-reply-prod"
journalctl -u velvetposter --no-pager -n 50
Timeout: 60s
Confirm.
[✅ Yes] [❌ No]
Claude Code clicked Yes. Every time it needed to. Just like I would.
At one point it tried something requiring sudo. The bot refused — not because it recognized that an AI was asking, but because the agent user doesn't have sudo rights. Same result I'd get. The harness doesn't have a special mode for AI agents. It just has the same rules for everyone who connects.
I didn't design it this way intentionally. I designed it for people like me — non-DevOps founders running production servers who need a confirmation step before anything destructive happens, because without one, things go wrong in ways that are hard to trace. The fact that this design also works for AI agents is a consequence, not a feature. An interface simple enough for someone with no sysadmin background turns out to be simple enough for an agent to operate correctly without special handling.
What this means for AI on production
The question everyone is asking right now is some version of: how do you let AI agents touch production without catastrophe?
The answer I've landed on is boring: you build the same interface you'd build for a cautious human, and you make the agent use it. Not SSH. Not raw API access. The interface.
The benefit isn't just safety — it's that the agent sees what the user sees. Ninety-one incidents. Auto-remediation running. A service that looked healthy from the outside and was silently failing once an hour. That context lives in the monitoring layer, not in the filesystem. You only get it if you go through the right door.
If you're building something real and running it on a VPS without a dedicated ops team, mttrly connects to your server via Telegram and gives you a Watchdog tier free for one server. No SSH keys shared, no dashboard to remember. The bot sits in your Telegram and waits for something to go wrong.
When it does — whether it's you asking, or an AI agent you sent to investigate — it'll tell you what it found, propose what to do, and wait for confirmation.
That's the whole design. It just took me a while to stop bypassing it.
Try the free Watchdog tier at mttrly.com — or read more about how it works for vibe-coders like me.