PLATINUM DOCS

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 → sandboxTerminal 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: websocket

Before 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_TOKEN instead — 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

CodeMeaning
1000shell exited
1008sandbox belongs to another org
1011unknown sandbox, not running, or upstream error — the reason string says which
1012the 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