Terminal & SSH
Open an interactive shell in a sandbox — dashboard, CLI, or raw WebSocket — and inject SSH keys.
The terminal gives you an interactive shell inside a sandbox. Use it from the dashboard, the pt CLI, or any WebSocket client. For programmatic commands, use exec.
Open the web terminal
Open the dashboard → sandbox → Terminal tab. This gives an in-browser shell on any running sandbox. It runs over the WebSocket endpoint below.
Open an interactive shell
The endpoint is a raw byte pipe to a shell in the sandbox:
GET wss://api.platinum.dev/v1/sandboxes/:id/terminal
Upgrade: websocketBefore you connect, note:
- The sandbox must be
running. - The terminal opens at 80×24. It cannot resize.
- The SDKs have no terminal wrapper. Connect any WebSocket client.
- Authenticate with
Authorization: Bearer $PT_TOKEN. Browsers cannot set headers on a WebSocket upgrade. Pass?token=$PT_TOKENinstead — only this endpoint accepts it. The dashboard uses your session cookie.
// no SDK wrapper — connect any WebSocket client, for example "ws"
import WebSocket from "ws";
const ws = new WebSocket(
`wss://api.platinum.dev/v1/sandboxes/${id}/terminal`,
{ headers: { Authorization: `Bearer ${process.env.PT_TOKEN}` } },
);
ws.on("open", () => {
process.stdin.setRawMode(true);
process.stdin.on("data", (b) => ws.send(b));
});
ws.on("message", (data) => process.stdout.write(data as Buffer));
ws.on("close", () => process.exit(0));# no SDK wrapper — connect any WebSocket client, for example "websockets"
import asyncio, os, sys, websockets
async def shell(sandbox_id: str):
url = f"wss://api.platinum.dev/v1/sandboxes/{sandbox_id}/terminal"
hdrs = {"Authorization": f"Bearer {os.environ['PT_TOKEN']}"}
async with websockets.connect(url, additional_headers=hdrs) as ws:
await ws.send("uptime\n")
async for chunk in ws:
out = chunk if isinstance(chunk, bytes) else chunk.encode()
sys.stdout.buffer.write(out)
sys.stdout.buffer.flush()
asyncio.run(shell("sbx_..."))pt sandbox ssh sbx_123 # alias: pt sandbox shell
echo "uptime" | pt sandbox ssh sbx_123 # pipe stdin to run a script# curl cannot upgrade to WebSocket — use a WS client: wscat -c "wss://api.platinum.dev/v1/sandboxes/$ID/terminal" -H "Authorization: Bearer $PT_TOKEN"Output is the raw shell stream. Feed it straight to an xterm. When the socket closes, the shell dies. When the shell exits, the socket closes.
Keystrokes and output count as auto-stop activity. An idle terminal does not keep the sandbox alive.
Close codes
| Code | Meaning |
|---|---|
1000 | shell exited |
1008 | sandbox belongs to another org |
1011 | unknown sandbox, not running, or upstream error — the reason string says which |
1012 | the API is restarting — reconnect immediately |
Inject SSH keys
Pass OpenSSH public keys at create, or replace the set later. Keys land in /root/.ssh/authorized_keys.
Before you call:
- Each call replaces the whole set. Send the complete list every time. To rotate a key out, call again without it.
- The sandbox must be
running. - Before you publish a template from a sandbox with injected keys, snapshot with
clear_ssh_keys: true— see snapshots.
const sbx = await client.sandboxes.create(
{ template: "my-sshd-template", ssh_keys: ["ssh-ed25519 AAAA… you@laptop"] },
{ waitForRunning: true },
);
await sbx.addSshKeys(["ssh-ed25519 AAAA… you@laptop", "ssh-ed25519 AAAA… ci"]);sbx = client.sandboxes.create(
template="my-sshd-template",
ssh_keys=["ssh-ed25519 AAAA… you@laptop"],
wait_for_running=True,
)
sbx.add_ssh_keys(["ssh-ed25519 AAAA… you@laptop", "ssh-ed25519 AAAA… ci"])pt api POST /v1/sandboxes/$ID/ssh-keys \
-d '{"keys": ["ssh-ed25519 AAAA… you@laptop"]}'curl -X POST "$PT_API_URL/v1/sandboxes/$ID/ssh-keys" \
-H "Authorization: Bearer $PT_TOKEN" \
-d '{"keys": ["ssh-ed25519 AAAA… you@laptop"]}'The default template does not run an SSH server. To use these keys, your template must install and start openssh-server. There is no public SSH gateway, and preview URLs carry HTTP only. To get a shell, connect the terminal WebSocket above, not a plain ssh client.
See also
- Exec — programmatic command execution
- Filesystem — file transfer without scp
- Networking — expose ports, preview URLs, egress policy
- API reference