CLI
pt drives sandboxes, templates, volumes, secrets, webhooks and API keys from a terminal. It also runs the MCP server for coding agents.
Install
npm i -g @platinum-dev/cli # Node 18 or newer
pt init # signs in if needed, then prints next stepspt update installs the latest published version. npx @platinum-dev/cli init runs it without a global install.
Authentication and settings
pt login opens the console to approve a key and saves it to ~/.config/platinum/credentials (mode 0600), or under $XDG_CONFIG_HOME/platinum/ when that is set. In CI, set the key in the environment instead. It wins over the file and never touches disk.
export PLATINUM_API_KEY=pt_live_... # or PT_TOKEN
export PT_API_URL=https://api.example.com # or PLATINUM_API_URLSettings resolve as flag, then environment, then profile, then default. Profiles live in config.toml in the same directory. The default API URL is http://localhost:3000.
| Variable | Description |
|---|---|
PLATINUM_API_KEY, PT_TOKEN | API key (pt_live_...) |
PLATINUM_API_URL, PT_API_URL | Control plane URL; PLATINUM_API_URL wins if both are set |
PLATINUM_PROFILE | Profile to use |
PLATINUM_REGION | Default --region; overrides the profile region |
PLATINUM_OUTPUT | table, json or yaml |
PLATINUM_WEB_URL | Console URL that pt login opens |
PLATINUM_DEBUG | Log HTTP requests to stderr |
PLATINUM_NO_UPDATE_NOTIFIER | Skip the update check; also skipped in CI and without a TTY |
NO_COLOR | Disable colour |
Global flags
Every command accepts these, before or after the subcommand.
| Flag | Default | Description |
|---|---|---|
--profile <name> | set by pt config use, else default | Named profile |
--api-url <url> | http://localhost:3000 | Control plane URL |
-o, --output <fmt> | table | table, json or yaml |
-q, --quiet | off | Status lines off; pt sandbox create -q prints only the id |
--no-input | off | Never prompt |
--debug | off | Log HTTP to stderr |
Status lines go to stderr and data to stdout, so pt sandbox list -o json | jq . stays clean. pt <command> --help lists every argument.
Account and setup
pt init # sign in if needed, print next steps
pt login # approve a key in the browser
pt logout # remove the current profile's stored key
pt configure [--token] [--region] # set a key and defaults for a profile
pt config url|web|set|get|list|use
pt whoami # identity and organization of the current key
pt open # open the console
pt regions # valid --region values
pt completion bash|zsh|fish # print a completion script
pt update [--dry-run] # alias: pt upgradept login
| Flag | Default | Description |
|---|---|---|
--token | none | Store an existing pt_live_ key; skip the browser |
--no-browser | off | Paste a key created in the console |
--org | none | Organization (id, slug or name) to preselect |
pt init takes --token and --no-browser too. pt config set <key> <value> takes url, web, region or output.
pt sandbox
get and exec accept a sandbox id or name. Every other command takes the id.
pt sandbox create [flags] # create, wait for running
pt sandbox list | ls [flags]
pt sandbox get <id>
pt sandbox children <id> # sandboxes created with --parent <id>
pt sandbox exec <id> '<cmd>' # or: exec <id> -- prog arg...
pt sandbox run-code <id> [code] [--lang python|node|bash|sh] [--file f]
pt sandbox ssh | shell <id> # interactive PTY, no keys needed
pt sandbox wait <id> [--state running] [--timeout 120]
pt sandbox stop | start | pause | resume | archive <id>
pt sandbox rename <id> [name] [--clear]
pt sandbox resize <id> [--cpu] [--ram MB] [--disk GB]
pt sandbox metrics <id> # live CPU, memory and disk
pt sandbox rm | delete <id>
pt shell [flags] # throwaway sandbox, deleted on exitA stopped sandbox can change all three resize flags. A running one can only grow --disk.
pt sandbox create
| Flag | Default | Description |
|---|---|---|
-t, --template | pt-base if ready | Template name or id; else the organization's first ready template |
--name | none | Display name |
--cpu | template default | vCPUs |
--ram | template default | RAM in MB |
--disk | template default | Disk in GB |
--region | profile region | Region from pt regions |
--runtime | microvm | Runtime, from pt api /v1/runtimes |
--parent | none | Create as a child of this sandbox |
--pool | general fleet | Host pool to place on |
--pool-fallback | any | none fails when the pool is full |
--env | none | K=V,K2=V2 |
--secret | none | Org secret name or sec_ id; repeatable |
-v, --volume | none | NAME:/mount[:ro][:subdir=/path]; repeatable |
--no-wait | off | Return before running |
--timeout | 120 | Seconds to wait for running |
--cpu, --ram and --disk cannot go below the template's defaults, and neither can resize.
pt sandbox create -t pt-base -q # sbx_01J...pt sandbox list
| Flag | Default | Description |
|---|---|---|
--state | all | Filter by state, such as running |
--runtime | all | Filter by runtime |
--parent | none | Only children of this sandbox |
--roots | off | Only sandboxes without a parent |
--limit | 50 | Max rows |
pt sandbox exec
exec prints the command's output and exits with its status. A command killed at its deadline exits 124.
| Flag | Default | Description |
|---|---|---|
--timeout | 30 | Deadline in seconds; the API caps one call at 85 |
pt sandbox exec sbx_01J... 'uname -r' # the guest's own kernelRun longer jobs as a background process through the SDK or POST /v1/sandboxes/{id}/processes.
pt shell
| Flag | Default | Description |
|---|---|---|
-v, --volume | none | NAME (mounts at /mnt/NAME) or NAME:/mount[:ro][:subdir=/path]; repeatable |
-t, --template | pt-base if ready | Template name or id |
--region | profile region | Region |
--keep | off | Keep the sandbox after the shell exits |
--timeout | 120 | Seconds to wait for boot |
pt sandbox files
pt sandbox cp <src> <dst> # ./f <id>:/path or <id>:/path ./f
pt sandbox fs ls <id> [path] # default /
pt sandbox fs read <id> <path> # to stdout
pt sandbox fs write <id> <path> [text] # text, stdin or --from
pt sandbox fs rm <id> <path> [-r]
pt sandbox fs mkdir <id> <path> # creates missing parents
pt sandbox fs stat <id> <path>
pt sandbox fs find <id> <glob>
pt sandbox fs grep <id> <pattern> # file:line:content per match
pt sandbox fs replace <id> <find> <replace>
pt sandbox fs watch <id> [path] # change events until --max-secondsA cp destination that is a directory (<id>:/workspace/, ./out/) keeps the source file's name.
| Command | Flag | Default | Description |
|---|---|---|---|
fs write | --from | stdin | Local file to upload |
fs rm | -r, --recursive | off | Remove directories |
fs find, fs grep | --path | /workspace | Root path |
fs find, fs grep | --max | 500 | Result cap; up to 5000 |
fs replace | --path | /workspace | Root path |
fs replace | --glob | all files | Limit to matching files, such as '*.py' |
fs watch | --interval | 1000 | Poll interval in ms; 500–10000 |
fs watch | --max-seconds | 300 | Stream lifetime; 5–3600 |
pt sandbox ports and network
pt sandbox expose <id> <port> # prints the preview URL
pt sandbox open <id> [port] # expose if a port is given, open in the browser
pt sandbox unexpose <id> <port> # kills every token for that port
pt sandbox revoke <id> <token> [--reason] # kills one token, the port stays exposed
pt sandbox network <id> [flags] # egress policy
pt sandbox egress <id> --mode allow|deny [--cidrs ...]
pt sandbox egress-clear <id>
pt sandbox secrets <id> # attached org secrets, never valuesegress and egress-clear set the older CIDR-only policy. Use network for new work.
pt sandbox expose
| Flag | Default | Description |
|---|---|---|
--tcp | off | Raw TCP port, always public, printed as host:port |
--proxy-protocol | off | With --tcp: send the guest a PROXY v1 line |
--direct | off | With --tcp: also open the port on the sandbox host and print that address |
pt sandbox expose sbx_01J... 8080 # https://8080-01j....sbx.example.com/?t=...pt sandbox network
| Flag | Default | Description |
|---|---|---|
--allow-out | none | Comma-separated IPs, CIDRs, hostnames or *. wildcards |
--deny-out | none | Comma-separated IPs or CIDRs; all means 0.0.0.0/0 |
--no-internet | off | Block all outbound; do not combine with --allow-out |
--header | none | host:Name:Value header to inject; repeatable |
--show | off | Print the policy in force and exit |
To allow only some hosts, use --deny-out all --allow-out <hosts>. Hostnames in --allow-out need --deny-out all.
pt sandbox network sbx_01J... --deny-out all --allow-out pypi.org,files.pythonhosted.orgpt sandbox snapshots and backups
pt sandbox snapshot <id> # disk only; memory and processes are not captured
pt sandbox snapshots <id>
pt sandbox snapshot-rm <id> <snapshot>
pt sandbox restore <id> # roll back, latest snapshot by default
pt sandbox fork <id> # snapshot now, start a copy
pt sandbox clone <id> # new sandbox from a snapshot
pt sandbox backup <id> # filesystem to object storage, in the background
pt sandbox restore-from-backup <id> # cold-boot from the latest backup| Command | Flag | Default | Description |
|---|---|---|---|
restore | --snapshot | latest | Snapshot id |
restore | --no-wait | off | Return before running |
restore | --timeout | 120 | Seconds to wait |
fork | --name | none | Name for the fork |
clone | --snapshot | latest | Snapshot id |
pt template
pt template build <name> --image <ref> | --repo <url> [flags]
pt template list | ls
pt template get <id|name>
pt template update <id> [flags] # metadata only; rebuild to change the image
pt template rm | delete <id|name>pt template build
Pass exactly one of --image or --repo.
| Flag | Default | Description |
|---|---|---|
--image | none | Container image to build from |
--repo | none | http(s) git URL with a Dockerfile |
--ref | none | Git branch, tag or sha |
--subdir | none | Build context inside the repo |
--dockerfile | none | Path relative to the context |
--token | none | Access token for a private repo; never stored |
--entrypoint | none | Override, with --image only |
--cpu | 1 | Default vCPUs for sandboxes |
--ram | 512 | Default RAM in MB |
--disk | 2 | Default disk in GB |
--size-mb | 1024 image, 8192 repo | Max rootfs size in MB |
--region | none | Pin the build and template to a region |
--global | off | Visible to every organization; admin only |
--wait | off | Wait until ready |
--timeout | 1800 | Seconds to wait |
pt template build my-tpl --image ubuntu:24.04 --waitpt template update
| Flag | Default | Description |
|---|---|---|
--version | unchanged | Version string |
--cpu | unchanged | Default vCPUs |
--ram | unchanged | Default RAM in MB |
--disk | unchanged | Default disk in GB |
--entrypoint | unchanged | Entrypoint |
pt vol
pt volume is an alias. Every pt vol command needs object storage on the control plane and fails with 503 without it.
pt vol create <name> [--mode drive|git]
pt vol list [--limit] [--all]
pt vol info <name> # with live mounts
pt vol mounts <name>
pt vol rename <old> <new> # mounts keep working
pt vol delete <name> # refused while mounted
pt vol mode <name> [drive|git]
pt vol limits # the organization's limits and usage
pt vol ls | cat | stat NAME:/path
pt vol cp <src> <dst> # local, - (stdio) or NAME:/path on either side
pt vol mv NAME:/a NAME:/b
pt vol rm NAME:/path
pt vol log | diff | tag | restore | fork <name> ...
pt vol attach <sandbox> NAME:/mount[:ro][:subdir=/path]
pt vol detach SANDBOX_ID:/mount # commits first
pt vol publish <name> # commit every writable mount, print the commit
pt vol wait <name> <commit> # until mounts show that commit
pt vol commit | pull | status <path> # inside a sandbox, or SANDBOX_ID:/data from outsideNames are up to 63 letters, digits, ., _ or -, starting with a letter or digit. Names starting with vol_ or tmp-, and limits, are reserved.
| Command | Flag | Default | Description |
|---|---|---|---|
create | --mode | drive | drive syncs on its own; git needs commit and pull |
list | --limit | all | Max rows |
list | --all | off | Include temporary volumes |
delete | --missing-ok | off | Succeed when the volume does not exist |
ls | -r, --recursive | off | Walk the whole tree |
ls, cat, stat | --at | head | Read at a commit id or tag |
cp | -r, --recursive | off | Copy directories |
cp | --overwrite | off | Replace existing files on the volume |
cp | --at | head | Read the source at a commit id or tag |
mv | --overwrite | off | Replace an existing destination |
rm | -r, --recursive | off | Remove a directory and its contents |
detach | --no-commit | off | Discard pending changes instead of committing |
publish, wait | --mount | every mount | Only this mount (vm_ id) |
wait | --timeout | 60 | Seconds |
log | --limit | 20 | Max commits |
log | --before | none | Page cursor from a previous listing |
log | --side | off | Include commits merged rather than landed on head |
diff | --path | volume root | Only changes under this directory |
tag | -d, --delete | off | Remove the tag; no tag argument lists tags |
fork | --at | head | Commit id or tag to fork from |
pt vol cp -r ./dataset data:/dataset # creates the volume `data`
pt sandbox create -v data:/data -v ref:/ref:ropt secret
Every command takes the secret's name or sec_ id. Values come from a hidden prompt or stdin, never a flag.
pt secret create <name> --allow <hosts> --header <names> [flags]
pt secret list | ls [--limit] [--cursor]
pt secret get <id> # with live bindings
pt secret update <id> [flags] # never the value
pt secret rotate <id> # new value; bound sandboxes pick it up
pt secret rm | delete <id> # revokes every binding
pt secret bind <secret> --sandbox <id> [--alias <key>]
pt secret unbind <sb_id>printf %s "$KEY" | pt secret create search-api --allow api.example.com --header authorization| Command | Flag | Default | Description |
|---|---|---|---|
create, update | --allow | required on create | Exact hostnames the value may be sent to; no wildcards |
create, update | --header | required on create | Request headers the value is written into |
create, update | --description | none | What the credential is for |
create, update | --on-echo | block | block or redact when a response echoes the value |
update | --name | unchanged | Rename |
list | --limit | 50 | Page size; up to 200 |
list | --cursor | none | Cursor from a previous page |
bind | --sandbox | required | Sandbox id |
bind | --alias | none | Key the binding is addressed by; not an env var in the guest |
On update, --allow and --header replace the stored list.
pt key
pt key create --name <name> [--scope ...] [--expires-in <days>]
pt key list | ls
pt key rm | delete <id> # revokes immediately
pt key scopes # scopes and presets| Flag | Default | Description |
|---|---|---|
--name | required | Key name |
--scope | * | Comma-separated scopes, such as sandboxes:read,templates:* |
--expires-in | never | Days until the key expires |
WARNING
pt key create prints the key once. Save it before you close the terminal.
pt webhook
pt webhook create --url <https-url> [--events ...] # secret shown once
pt webhook list | ls
pt webhook update <id> [flags]
pt webhook test <id> # sends a ping
pt webhook deliveries <id> [--status ...]
pt webhook retry <id> <delivery> # whd_ id
pt webhook rm | delete <id>| Command | Flag | Default | Description |
|---|---|---|---|
create | --url | required | Endpoint URL |
create | --events | * | Comma-separated events |
update | --enable, --disable | unchanged | Resume or pause delivery |
update | --url, --events | unchanged | New endpoint or events |
deliveries | --status | all | pending, success, failed or dead_letter |
pt audit
pt audit list | ls [flags] # organization owners and admins only| Flag | Default | Description |
|---|---|---|
--action | all | Action prefix, such as sandbox. |
--actor | all | Actor user id |
--status | all | ok, denied or error |
--limit | 50 | Max rows; up to 200 |
pt mcp
pt mcp start [flags] # MCP server on stdio
pt mcp init claude|cursor|windsurf # wire it into an agent
pt mcp config [agent] [--with-token] # print the config snippet| Command | Flag | Default | Description |
|---|---|---|---|
start | --permissions | read,exec,write | Tool groups to register; manage is opt-in. Env PT_MCP_PERMISSIONS |
start | --readonly | off | Register read tools only |
start | --sandbox | none | Pin default-sandbox tools to this id. Env PT_MCP_SANDBOX |
start | --confine-sandbox | none | Reject every other sandbox id. Env PT_MCP_CONFINE_SANDBOX |
start | --allow-public-expose | off | Permit public preview URLs. Env PT_MCP_ALLOW_PUBLIC_EXPOSE |
init | --global | project .mcp.json | claude: register for the user |
init | --project | ~/.cursor/mcp.json | cursor: write ./.cursor/mcp.json |
config | --with-token | off | Embed PT_TOKEN and PT_API_URL in plain text |
AI agents and MCP covers the tools.
pt api
pt api sends an authenticated request to any endpoint and prints the JSON.
pt api [METHOD] <path> [flags]| Flag | Default | Description |
|---|---|---|
-X, --method | GET, or POST with --data | HTTP method |
-d, --data | none | JSON body: literal, @file, or - for stdin |
--query | none | Query string, such as state=running&limit=20 |
pt api POST /v1/sandboxes -d '{"template":"pt-base","cpu":2}'See also: Guides · HTTP API · AI agents and MCP