SSH
Log in to a running sandbox as root, at a port of its own.
For your agent
agental.io/docs/ssh.mdAn SSH connection goes from your machine straight to the server the sandbox is on, not through the API. Whether a sandbox can be reached, and who may log in to it, are two things, and each has calls of its own.
Turn it on
curl -X PUT https://agental.io/v1/sandboxes/{id}/ssh \
-H "Authorization: Bearer $AGENTAL_API_KEY"{
"enabled": true,
"enabledAt": "2026-10-13T09:02:11.480Z",
"address": "203.0.113.7:22041",
"host": "203.0.113.7",
"port": 22041,
"user": "root",
"hostKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI…"
}- The sandbox has to be running. Asked again, the call changes nothing that is already so.
addressis where to connect now, withhostandportbeside it. The port leads to this sandbox's SSH server and to nothing else.- It lets no key in. That is the next call.
- A machine that has no SSH server is given one the first time, and that request takes as long as the install does.
Let a key in
curl -X POST https://agental.io/v1/sandboxes/{id}/ssh/keys \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"publicKey":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI… [email protected]"}'publicKeyis one line, as it stands in your~/.ssh/id_ed25519.pub.- The answer has the key's
id, itsfingerprintasssh-keygen -lprints it, itstypeand itscomment. - A key that is already let in is not added twice.
GET /v1/sandboxes/{id}/ssh/keyslists the keys, andDELETE /v1/sandboxes/{id}/ssh/keys/{keyId}takes one out. Whoever is logged in with it stays until they leave.- The keys are the machine's own
/root/.ssh/authorized_keys. A key written there from inside the sandbox is listed like any other. - A fork has its source's keys, and a roll-back puts back the ones its snapshot had.
Keys and SSH as a sandbox is made
A sandbox can be made with keys already let in, and with SSH already on, so that it can be logged in to as soon as the create answers.
curl -X POST https://agental.io/v1/sandboxes \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Idempotency-Key: agent-1" \
-H "Content-Type: application/json" \
-d '{"name":"agent-1","sshKeys":["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI… [email protected]"]}'sshKeyson a create lets those keys in as the machine is made, before the answer. It turns nothing on.- A template lets keys in to every sandbox made from it:
POST /v1/templates/{id}/ssh/keys, with the same body as above, andDELETE /v1/templates/{id}/ssh/keys/{keyId}to take one out. A create's own keys come on top of its template's. While your account has two-factor sign-in on, a key new to the template, let in from the console or sent by a person signed in to the site rather than with an API key, takes acodeas well. PUT /v1/templates/{id}/sshturns SSH on for each sandbox as it is made, andDELETEturns that off.GET /v1/templates/{id}/sshsays both, and the keys.- A sandbox made already is not changed by its template's keys or SSH. A key taken out of a template stays in the sandboxes made from it until it is taken out of each.
- A create with keys takes the API key action
ssh, as letting one in later does. So do the template's calls, apart from reading. - Where a sandbox is made with SSH on and no port can be opened to it, the create still succeeds: SSH is on, and
PUT /v1/sandboxes/{id}/sshopens the port.
Connect
Pin the host key the API gave you, then log in. With the answer above:
echo "sbx_2a9a47e50daec64a ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI…" > ~/.ssh/known_hosts_sbx_2a9a47e50daec64a ssh -p 22041 \ -o UserKnownHostsFile=~/.ssh/known_hosts_sbx_2a9a47e50daec64a \ -o StrictHostKeyChecking=yes \ -o HostKeyAlias=sbx_2a9a47e50daec64a \ [email protected]
hostKeycomes over the API and not over SSH, so you never have to accept a key you are shown.- Pin it under the sandbox's id, as above, and not under the address: an address and a port are another sandbox's in time.
- Only a key that was let in logs in. The SSH server takes no passwords.
scp,rsyncandgitwork at the same port.- Only port 22 of the sandbox is reached this way. Its other ports are public URLs, or forwarded through SSH itself.
After a stop or a move
SSH stays on for the sandbox. Its port is closed when the sandbox stops, and opened again by itself a moment after it runs again. To read where it is now:
addressis null while the sandbox is not running, and for a moment after a start.- The address is mostly the same after a start. It is another one once the sandbox has been moved to another server, and connections that were open are lost at a move.
- So a client that loses its connection reads this call again, until it shows an address, and connects there.
- The host key stays the same through a stop and a start, and through a move.
Turn it off
Closes the port, which cuts whoever is connected. The keys are left as they are.
In the console
A sandbox's Network view has SSH under In, after its ports. It turns SSH on and off, shows the command to log in with and the host key, and lists the keys let in, each with a way to take it out, above a field that lets one in. The keys are read from the machine, so they show while the sandbox runs.
Errors
| Answer | Meaning |
|---|---|
409 sandbox_not_running | The sandbox is stopped. Start it, then repeat the request. |
409 ssh_unavailable | The machine has no SSH server and one could not be installed or started, or no port could be opened to it. message says which. |
400 invalid_request | publicKey is not one OpenSSH public key line. |
501 ssh_endpoint_not_configured | Sandboxes here are not logged in to over SSH. GET /v1/policy says so as ssh. |
The full machine-readable reference is OpenAPI 3.1. Every page as Markdown, for an agent: llms.txt. Questions: [email protected]