An AI agent on your server: approve, then audit
An agent that can run commands on your server can also lock you out, delete data or read a key it never needed. Two checks cover it: before a change, it asks you and a rollback timer waits behind it; after, you read what it ran. Stenvakt does the first, ranwhat does the second, and each works without the other.
Published . Claude Code, Codex or Cursor driving a Linux server you run yourself. Ubuntu and Debian are the tested ones. Stenvakt is for Mac; ranwhat runs on macOS and Linux with Python 3.9 or newer.
- Give it reading first
- Make every change a proposal
- Keep your keys out of its reach
- Audit what it ran
- A routine that takes five minutes
- What this does not cover
The usual way to let an agent near a server is the worst one: paste the SSH command into the chat and let it run. From then on it holds your key, your shell and your server, and the only record of what it did is a chat scrollback. A better arrangement has three parts: what the agent may read, what it may change, and how you check afterwards.
Give it reading first
Most of what you want from an agent on a server is reading: which ports are open, which updates are pending, who logged in, whether a backup exists. Reading changes nothing, so it can run without asking. Give the agent a tool that only reads, and it can answer those questions without a shell.
That is what the Stenvakt MCP connection is for. Claude Code, Codex and Cursor connect to the app over MCP and can list your servers, run the review and preview a fix. No SSH key, password or token goes to the agent: the app on your Mac holds the connection.
Make every change a proposal
A change is different: a firewall rule, an SSH setting, an update, a reboot. Here the agent should propose and you should approve. In Stenvakt, an agent's change waits in the app until you accept it, with the commands it would run shown first. The exception is a server you mark as a test server, where agent changes apply without asking. Use that for a throwaway machine, not for production.
Approval is not the only protection. Firewall and SSH steps, the ones that can cut you off, run with safe mode: the server arms its own rollback timer, the app tries a brand-new login, and if that login fails the server puts the old configuration back. Agents cannot turn safe mode off. More on that in SSH hardening without lockout.
The rule underneath: the agent proposes, a person decides, the server can undo. If one of the three is missing, the setup depends on nothing going wrong.
Keep your keys out of its reach
If an agent has a shell on your Mac, it can read anything your user can: ~/.ssh, .env files, cloud credentials. Whatever it reads goes into a model context you do not control. Two habits cut that down:
- Use a tool, not a shell. With an MCP tool that holds the connection, the agent never needs the key at all.
- Use a separate key for anything an agent touches directly. A key with its own name in
authorized_keyscan be removed in one line without disturbing anyone else, and it shows up under its own name injournalctl -u ssh.
Audit what it ran
Checks before the change are half of it. The other half is knowing afterwards what the agent did, including the commands that never went through an approval screen: a rm -rf, a cat ~/.ssh/id_rsa, a force push. Agents already write their own history to disk, so there is a record to read.
ranwhat reads that record. It works from the transcripts Claude Code, Codex and other agents have already saved on your machine, so there is nothing to wrap or proxy and nothing leaves the machine. Run it once, without installing:
uvx ranwhat check
Or look at a window of time, for example after a session where an agent worked on a server:
ranwhat watch --days 7
It lists the actions worth a second look: credential material read, recursive deletion, destructive git operations, packages published. Each comes with the command and the time. ranwhat check --fail-on high exits non-zero when it finds something at that level, so a script or CI job can fail on it.
The two tools check different moments. Stenvakt sits in front of a server change; ranwhat reads behind the agent, on your Mac, whatever the agent ran. If you give an agent both a Stenvakt connection and a shell, the second is the one that will surprise you, and the one to read.
A routine that takes five minutes
- Take a snapshot or a database backup first (snapshot before changes, backups before changes).
- Let the agent read and propose through the app. Read the commands in each proposal before you accept.
- Accept the changes you want. Leave safe mode on.
- When the session is over, run
ranwhat watch --days 1and read the list. - Check
journalctl -u ssh --since todayon the server for logins you do not recognise.
What this does not cover
Approval does not make a bad proposal good: read it. Safe mode covers firewall and SSH steps, not every change; an update or a deleted file has no timer, which is why the backup comes first. And ranwhat reads what the agents wrote down, so an agent that does not keep a history, or a history that has been cleaned up, leaves nothing to read.
Stenvakt reviews your servers and gates what an agent can change, free to download for Mac. For the other half, ranwhat reads what your agents already ran, locally and without an account.
Sources
- Model Context Protocol: specification
- ranwhat: flight recorder and authority scanner for AI coding agents
- OpenBSD manual: ssh(1) (BatchMode, IdentityFile, ForwardAgent)