# How to open an interactive terminal in a sandbox Run `runtime sandbox shell ` from your terminal, or call `sbx.terminal()` in code for a real terminal over a WebSocket. **On Runtime the terminal is a real one, with colours, prompts and resizing, and the sandbox opens no port to get it.** It travels over one authenticated WebSocket through Runtime's API, so only a key that may run commands in that sandbox can connect. A paused sandbox wakes for it by itself, usually in about half a second, and a pause took a median 234 ms when you are done, measured on 24 September 2026 ([speed](/docs/speed)). ## From your own terminal ```bash no-run runtime sandbox shell "${id}" # an interactive terminal, like ssh runtime sandbox ssh "${id}" # the same through OpenSSH, as the sandbox user runtime sandbox ssh "${id}" -- make test # one command; exits with its code ``` `${id}` can be the sandbox's id or its name. `ssh` makes an SSH key for this machine on first use and sends only its public half. Run `runtime sandbox ssh config --install` once and every sandbox answers as the host `.runtime` to ssh, scp, VS Code and JetBrains Gateway ([SSH and editors](/docs/editors)). ## From code What you write is typed into the terminal; what it prints comes back as bytes, escape codes included: ```ts import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create(); const term = await sbx.terminal({ cols: 120, rows: 40, onData: (bytes) => process.stdout.write(bytes), }); term.write("echo hello from the terminal\n"); term.resize(100, 30); term.write("exit\n"); console.log("exit code", await term.exited); ``` ```python from withruntime import Sandbox with Sandbox.create() as sbx: term = sbx.terminal(cols=120, rows=40) term.write("echo hello from the terminal\n") term.write("exit\n") while (chunk := term.recv()) is not None: print(chunk.decode(errors="replace"), end="") ``` In TypeScript, `term.exited` resolves with the shell's exit code. In Python, `recv()` returns `None` once the terminal has closed. ## The protocol, if you speak it yourself `GET /v1/sandboxes/{id}/terminal` with `Upgrade: websocket` and the bearer header opens one ([terminals](/docs/api#terminals)). | Part | What it is | | -------------------- | ------------------------------------------------------------------------------ | | Query | `cols`, `rows`, `command` (default `bash -l`), `cwd`, or `processId` | | Binary frames | Terminal bytes, both ways | | Text frames you send | `{"type":"resize","cols":120,"rows":40}` | | Text frames it sends | `{"type":"ready","processId":...}`, then `{"type":"exit","exitCode":...}` | | Attach to a process | Start it with `spawn(..., { pty: { cols, rows } })`, then pass its `processId` | | Limits | 64 terminals open per organization, at most 12 of them in one sandbox | | Who you are | The `runtime` user, whose home is `/workspace`, with `sudo` | | Cost | Nothing for the terminal; the sandbox's measured CPU and reserved memory | ## Terminal, exec or SSH? | You want | Use | | ---------------------------------------- | -------------------------------------------------------- | | One command and its result | `exec`, which waits and returns the exit code and output | | A program that asks questions or redraws | `terminal()` or `runtime sandbox shell` | | Your editor, scp or rsync | `runtime sandbox ssh` and `ssh .runtime` | | A port on your laptop | `runtime sandbox port-forward ` | ## Mistakes to avoid - **Sending a command without a newline.** The terminal only runs what you type when it sees `\n`, as a keyboard's Enter. - **Parsing terminal output as plain text.** It carries colour and cursor codes. For output you mean to parse, run the command with `exec`, which returns clean `stdout` and `stderr`. - **Leaving terminals open.** An organization holds 64 at once, 12 in any one sandbox. Write `exit`, or close the socket, when a session ends; past the limit you get 429 with `Retry-After`. - **Expecting an open terminal to hold the sandbox awake forever.** The lease still ends at `timeoutSeconds`. Extend it, or see [extend a sandbox lease](/how-to/extend-a-sandbox-lease). ## Related - [Run a background process](/how-to/run-a-background-process) and attach a terminal to it. - [Per-user dev environments](/use-cases/per-user-dev-environments), where each user gets a shell of their own. - [Coding agent sandbox](/use-cases/coding-agent-sandbox) for agents that need a shell. - [An interactive terminal](/docs/javascript#an-interactive-terminal) in the SDK reference. Facts on this page were checked on 25 September 2026.