DEV Community

Svyatoslav Pavlov
Svyatoslav Pavlov

Posted on Originally published at termal.in on

Every way to give an AI agent SSH access, ranked by blast radius

Sooner or later you'll want an AI agent to touch a real server — tail a log, restart a service, run the deploy. Every way of arranging that answers the same underlying question: when the agent gets confused or compromised — prompt injection, a poisoned tool result, a plan that skipped a step — what does it cost you? That's the blast radius, and it's the right axis to rank the options on, because the failure isn't hypothetical. A model that reads untrusted input (logs, filenames, web pages, other tools' output) can be steered by it. You don't get to prevent that; you get to decide what it's worth when it happens.

Here are the five arrangements people actually use, worst to best. The tradeoffs of each are real, including the good ones.

1. Paste the private key into the agent's environment

The direct route: id_ed25519 in an env var, a config blob, a mounted file, so the agent can run ssh itself.

Blast radius: everything, forever. An SSH key is a pure bearer credential — whoever holds the bytes is you, on every host that trusts the key, with a full shell, from anywhere on the internet. And it's the one arrangement you can't walk back: once the key has passed through the model's context, a transcript, or a tool call you didn't read, you can't prove it didn't leak. The only honest remediation is rotating the key on every host that trusts it.

The honest upside: it works in thirty seconds with zero dependencies, which is why it's everywhere. That's the entire upside.

2. Forward your agent into the box the AI runs on

ForwardAgent yes into the VM or devbox where the coding agent lives, so anything there can authenticate as you without holding the key.

Blast radius: your full identity, live, for the duration of the session. The key bytes stay home — that's genuinely better than option 1, and when the session closes the exposure ends. But while it's open, any process on that box that can reach the forwarded socket can sign as you, to every host your keys reach, and nothing on your side shows it happening. You've lent the box live use of your identity; whether the borrower is the agent following instructions or something the agent was injected into is invisible from where you sit. We've written up the mechanics and the mitigationsssh-add -c at minimum, so every signature needs a local click.

The honest upside: revocation is closing the session, and no secret is ever at rest on the agent's side. As a supervised, short-lived arrangement it beats a pasted key by a wide margin. As a standing one, it's option 1 with a timer.

3. A raw SSH-MCP server holding a key, no policy

A standalone MCP server with an ssh_exec tool and a key (or password) in its own config. The model never sees the credential — it sees tools. This is the shape of most minimal SSH-MCP servers you'll find.

Blast radius: a full shell on every host the server's key reaches. Getting the key out of the model's context is a real improvement — a leaked transcript no longer contains a credential, and revocation means rotating one known key, not auditing everywhere the model's context went. But the capability is unbounded: any tool call can run anything, on any configured host, and a steered model doesn't need the key when ssh_exec("curl … | sh") is on the menu. These servers also tend to ship with no session visibility and no audit trail, so your record of what an agent did is whatever the agent says it did.

The honest upside: simple, self-hosted, credential out of the context window. The gap is that it treats key custody as the whole problem, when unbounded execution is most of it.

4. A scoped credential per task

The classical ops answer: a deploy key limited to one repo, a command=-restricted line in authorized_keys, a dedicated low-privilege user, or — the serious version — short-lived certificates minted per task with the principals and TTL baked in.

Blast radius: one task's worth of capability, expiring on its own. This is a genuinely strong position. A stolen deploy credential deploys; it doesn't read the production database two hops over. Short-lived certs mean stolen material goes stale in minutes, and command= restrictions bound even a live credential to one action.

The honest tradeoffs: the operational burden is real and it compounds. Somebody has to mint, distribute, and track every scoped credential; a certificate flow needs CA infrastructure most teams don't run; and scoped credentials sprawl — the forced-command entry for a task finished in March is still in authorized_keys in August, because revoking something requires remembering it exists. Under deadline pressure, "just this once" broadens the scope, and the scheme quietly degrades toward option 1. Observability is also not included: a scoped credential bounds what the agent can do, but records nothing about what it did.

5. A custodian with per-host policy, live watch, and audit

The agent never holds any credential. A custodian — a broker the human controls — holds the keys or mints the certificates, signs on the agent's behalf, and wraps execution in controls: the agent reaches only enrolled hosts, each host carries a command policy (full shell, an allowlist, or blocked), a human can watch the session live, and every command lands in an audit log attributed to the agent. The MCP explainer covers why "who holds the credential" is the load-bearing question; this is the arrangement where the answer is "not the agent, ever."

Blast radius: the policy, minus what output can carry. Compromise the agent's context entirely and what you hold is the ability to request allowed commands on listed hosts — requests that appear in a live view and an audit log as they happen. No credential to exfiltrate, revocation is a toggle, and the record distinguishes the agent's actions from yours.

The honest limits — because this option has them too:

  • Policy doesn't stop exfiltration through allowed output. If cat is on the allowlist, cat .env returns your secrets into the model's context, and from there they can go wherever the model's output goes. A custodian bounds actions, not the information content of results. Allowlists need to be written with reads in mind, not just writes — and secrets on disk are their own problem to fix.
  • A full-access host is still a full shell. The policy is only as tight as you set it; "full" on production recreates option 3 for that host.
  • The custodian is now the thing to protect. Compromise the machine it runs on and you're back to holding keys. Concentrating custody is the right trade — one hardened place beats copies everywhere — but it's a trade, not a free lunch.
  • Watching reduces dwell time, not probability. The live view and audit log mean a steered agent gets caught in minutes instead of discovered in a postmortem. Injection still happens; you've bounded what it's worth and how long it runs.

The ranking, compressed

What a compromise costs, top to bottom: (1) your identity, everywhere, unrevocably → (2) your identity, live, until you notice → (3) a full shell behind one revocable credential → (4) one task's capability, self-expiring, if the hygiene holds → (5) the policy you wrote, observed and revocable, minus whatever allowed output carries.

If you're assembling option 4 or 5 yourself from certificates, forced commands and tlog, that's a respectable project. The reason we built Termalin is that almost nobody finishes that project: it's an SSH client whose built-in MCP server is the custodian — keys stay in the one-unlock agent, per-host policy is a setting (full / allowlist / blocked), agent sessions mirror into a live watch grid, and every agent command is tagged in output-only recordings and the audit log. The same pattern works with a local model as with a hosted one, and the full custody story is written up on the security page.

Whatever you pick, pick it deliberately. The worst arrangement on this list is also the one you get by not choosing.


Termalin is a free, cross-platform SSH client with a built-in MCP server, a key custodian and per-host agent policy — download it, or read how it handles keys safely.

Top comments (0)