SSH Access via Proxy
How an LLM agent runs commands on remote machines safely through an SSH-Proxy API instead of raw SSH keys.
SSH Access via Proxy
Agents need to run commands on remote machines — deploy a container, read a log, fix a config. Handing raw SSH private keys to an LLM is a bad idea: keys end up in context windows, logs and transcripts. An SSH-Proxy solves this: it owns the credentials, the agent only speaks HTTP.
The Model
LLM ──HTTPS──► SSH-Proxy ──SSH──► target machines
(owns keys,
audit log,
rate limits)The proxy exposes persistent sessions with a simple REST surface:
| Endpoint | Purpose |
|---|---|
POST /connect?target=vps1 |
open a session → session_id |
POST /<sid>/exec {command, timeout} |
run a command (async) → exec_id |
GET /<sid>/exec/<exec_id> |
poll for output |
| SFTP endpoints | file up-/download, ls, mkdir, rm |
Auth is a Bearer token — the same pattern as every other API you use.
The Execution Pattern
# 1. connect once, reuse the session for the whole task
SID=$(curl -s -H "Authorization: Bearer $KEY" \
"https://ssh-proxy.example/connect?target=vps1" | jq -r .session_id)
# 2. run a command
EID=$(curl -s -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-X POST "https://ssh-proxy.example/$SID/exec" \
-d '{"command": "docker ps --format \"{{.Names}}\"", "timeout": 120}' | jq -r .exec_id)
# 3. poll the result
curl -s -H "Authorization: Bearer $KEY" "https://ssh-proxy.example/$SID/exec/$EID"Rules of Engagement
- Reuse sessions. Connecting per command wastes resources and trips
rate limits. Store the
session_id(e.g./tmp/vps1_sid) and heartbeat it before reuse. - Name targets, don't discover them. Targets are pre-registered
(
vps1,pi, …). The proxy is the single source of truth — never hardcode IP addresses in agent code. - Prefer SFTP over shell cat/heredoc for files. Escaping binary data
through
execcorrupts payloads. Use the dedicated upload/download endpoints. - Quote your commands. The JSON body is parsed twice (HTTP + shell). Wrap remote commands in single quotes and keep them one-liners.
- Assume auditing. Every exec lands in the proxy's audit log — behave as if the human reads everything (they can).
When NOT to use exec
- Reading structured state that has an API (Docker, databases, the Synapse Mind API) — always prefer the API over parsing CLI output.
- Long-running interactive processes.
execis fire-and-forget with a timeout; usenohup+ a status file on the target instead. - Anything destructive without an explicit instruction. The proxy gives you the power — the human gives you the permission.
Error Recovery
| Symptom | Fix |
|---|---|
| 401 on connect | token expired — re-read it from memory/settings, never from a transcript |
| session 404 | session expired (24h cap) — reconnect |
| exec timeout | raise timeout or split the command; check load on the target |
| garbled file content | you used exec instead of SFTP — switch to the SFTP endpoints |