Add an SSH connection
Adding a connection is three things: pin the host, seal the key, and declare the capabilities. The console does all of it in one form; the permaura CLI does it in one command; the REST API in a few calls.
1. Pin the host's fingerprint
The connection refuses to connect unless the server presents this exact key, so grab it first:
FP=$(ssh-keyscan -t ed25519 db1.example.com | ssh-keygen -lf - | awk '{print $2}')
echo "$FP" # SHA256:9Ije0/h/UFCjrsPr+a6o/dPtQhd0p9AbIQOW10Q7wJo
2. Create the connection
- Console
- permaura CLI
- REST API
Open Access → Connections → Add connection and choose SSH server. The form walks the same three steps:
- Name, Host, Port, Username — the SSH target, pinned server-side.
- Host key fingerprint — paste the
SHA256:…value from step 1. ("Skip host key verification" exists for a local dev box; don't use it across an untrusted network.) - Authenticate with — a private key (paste it or upload the file) or a password. Either is sealed on the gateway and never shown again.
- Allowed commands — one row per exec capability: an action name (
restart-app) and its command template (systemctl restart {service}). A{name}placeholder becomes the only thing an agent may fill in. - File access root — optional; exposes path-scoped
sftp.get/sftp.put/sftp.listunder that directory.
Save, and the connection appears on the Connections page with its capabilities listed — nothing is usable until a grant allows it.
One command creates the connection, seals the key, and grants it to an agent:
permaura connection add-ssh db1 \
--host db1.example.com --user deploy \
--host-key-sha256 "$FP" \
--key-file ~/.ssh/db1_ed25519 \
--sftp-root /var/data \
--exec 'uptime:uptime' \
--exec 'restart:systemctl,restart,{service}' \
--agent codex-cli
--exec 'action:program,arg,arg'defines one exec capability. A{name}argument becomes an allow-listed placeholder the agent may fill.--sftp-rootexposessftp.get/sftp.put/sftp.list, all scoped under that directory.- Add
--require-approvalto gate the exec commands on a human. (Writes andsftp.putare higher-risk and escalate automatically regardless.) - For a local or dev host with no fingerprint,
--insecureskips verification. Do not use it against a host reached over an untrusted network.
The same thing over REST v1 ($TOKEN is your operator token):
BASE=http://127.0.0.1:7376
# a) the connection: host/user/host-key pinned, capabilities declared
curl -X POST $BASE/v1/connections -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' -d '{
"id": "db1", "connector": "ssh", "name": "DB box",
"ssh": {
"host": "db1.example.com", "port": 22, "user": "deploy",
"host_key_sha256": "SHA256:9Ije…",
"sftp_root": "/var/data",
"exec": [
{ "action": "uptime", "args_template": ["uptime"] },
{ "action": "restart", "args_template": ["systemctl","restart","{service}"],
"allowed_placeholders": ["service"], "risk": "high", "requires_approval": true }
]
}
}'
# b) seal the private key (never returned)
curl -X POST $BASE/v1/connections/db1/credential -H "authorization: Bearer $TOKEN" \
-H 'content-type: application/json' -d "{\"value\": $(jq -Rs . < ~/.ssh/db1_ed25519)}"
# c) allow the actions and grant them to an agent
curl -X POST $BASE/v1/policies -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' -d '{
"connection_id": "db1", "allow": ["uptime","restart","sftp.list","sftp.get"] }'
curl -X POST $BASE/v1/grants -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' -d '{
"policy_ids": ["<the pol_… id returned above>"], "agents": [{ "agent_id": "codex-cli" }] }'
What you get
The connection now exposes these capabilities (namespaced under its id):
| Capability | What it does |
|---|---|
db1.uptime | runs uptime on the host |
db1.restart | runs systemctl restart <service> (approval-gated) |
db1.sftp.list | lists a directory under /var/data |
db1.sftp.get | reads a file under /var/data |
db1.sftp.put | writes a file under /var/data (higher-risk) |
Use it from an agent
Point an MCP client (Claude, ChatGPT, Cursor) at your gateway and the capabilities appear as tools named db1.uptime, db1.sftp.get, and so on. The agent calls them like any other tool:
db1.uptimewith{}returns the realuptimeline from the host.db1.sftp.getwith{ "path": "reports/q3.csv" }returns the file's bytes (base64), scoped under the root.db1.restartwith{ "service": "nginx" }returns awaiting approval and prompts you on your device; it runs only once you approve.
The key never leaves the gateway, and every call lands in the signed audit log.
Notes
- The key you seal must be unencrypted (this build authenticates with no passphrase). Generate a dedicated one with
ssh-keygen -t ed25519 -f ~/.ssh/db1_ed25519 -N ''and authorise it on the host. - Password auth is supported with
--password-env VAR(the value is read from that environment variable and sealed, never placed on a command line). - Check what is configured any time with
permaura connection list.