Skip to content

CLI ​

pt drives sandboxes, templates, volumes, secrets, webhooks and API keys from a terminal. It also runs the MCP server for coding agents.

Install ​

bash
npm i -g @platinum-dev/cli   # Node 18 or newer
pt init                      # signs in if needed, then prints next steps

pt 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.

bash
export PLATINUM_API_KEY=pt_live_...        # or PT_TOKEN
export PT_API_URL=https://api.example.com  # or PLATINUM_API_URL

Settings 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.

VariableDescription
PLATINUM_API_KEY, PT_TOKENAPI key (pt_live_...)
PLATINUM_API_URL, PT_API_URLControl plane URL; PLATINUM_API_URL wins if both are set
PLATINUM_PROFILEProfile to use
PLATINUM_REGIONDefault --region; overrides the profile region
PLATINUM_OUTPUTtable, json or yaml
PLATINUM_WEB_URLConsole URL that pt login opens
PLATINUM_DEBUGLog HTTP requests to stderr
PLATINUM_NO_UPDATE_NOTIFIERSkip the update check; also skipped in CI and without a TTY
NO_COLORDisable colour

Global flags ​

Every command accepts these, before or after the subcommand.

FlagDefaultDescription
--profile <name>set by pt config use, else defaultNamed profile
--api-url <url>http://localhost:3000Control plane URL
-o, --output <fmt>tabletable, json or yaml
-q, --quietoffStatus lines off; pt sandbox create -q prints only the id
--no-inputoffNever prompt
--debugoffLog 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 ​

text
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 upgrade

pt login ​

FlagDefaultDescription
--tokennoneStore an existing pt_live_ key; skip the browser
--no-browseroffPaste a key created in the console
--orgnoneOrganization (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.

text
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 exit

A stopped sandbox can change all three resize flags. A running one can only grow --disk.

pt sandbox create ​

FlagDefaultDescription
-t, --templatept-base if readyTemplate name or id; else the organization's first ready template
--namenoneDisplay name
--cputemplate defaultvCPUs
--ramtemplate defaultRAM in MB
--disktemplate defaultDisk in GB
--regionprofile regionRegion from pt regions
--runtimemicrovmRuntime, from pt api /v1/runtimes
--parentnoneCreate as a child of this sandbox
--poolgeneral fleetHost pool to place on
--pool-fallbackanynone fails when the pool is full
--envnoneK=V,K2=V2
--secretnoneOrg secret name or sec_ id; repeatable
-v, --volumenoneNAME:/mount[:ro][:subdir=/path]; repeatable
--no-waitoffReturn before running
--timeout120Seconds to wait for running

--cpu, --ram and --disk cannot go below the template's defaults, and neither can resize.

bash
pt sandbox create -t pt-base -q   # sbx_01J...

pt sandbox list ​

FlagDefaultDescription
--stateallFilter by state, such as running
--runtimeallFilter by runtime
--parentnoneOnly children of this sandbox
--rootsoffOnly sandboxes without a parent
--limit50Max rows

pt sandbox exec ​

exec prints the command's output and exits with its status. A command killed at its deadline exits 124.

FlagDefaultDescription
--timeout30Deadline in seconds; the API caps one call at 85
bash
pt sandbox exec sbx_01J... 'uname -r'   # the guest's own kernel

Run longer jobs as a background process through the SDK or POST /v1/sandboxes/{id}/processes.

pt shell ​

FlagDefaultDescription
-v, --volumenoneNAME (mounts at /mnt/NAME) or NAME:/mount[:ro][:subdir=/path]; repeatable
-t, --templatept-base if readyTemplate name or id
--regionprofile regionRegion
--keepoffKeep the sandbox after the shell exits
--timeout120Seconds to wait for boot

pt sandbox files ​

text
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-seconds

A cp destination that is a directory (<id>:/workspace/, ./out/) keeps the source file's name.

CommandFlagDefaultDescription
fs write--fromstdinLocal file to upload
fs rm-r, --recursiveoffRemove directories
fs find, fs grep--path/workspaceRoot path
fs find, fs grep--max500Result cap; up to 5000
fs replace--path/workspaceRoot path
fs replace--globall filesLimit to matching files, such as '*.py'
fs watch--interval1000Poll interval in ms; 500–10000
fs watch--max-seconds300Stream lifetime; 5–3600

pt sandbox ports and network ​

text
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 values

egress and egress-clear set the older CIDR-only policy. Use network for new work.

pt sandbox expose ​

FlagDefaultDescription
--tcpoffRaw TCP port, always public, printed as host:port
--proxy-protocoloffWith --tcp: send the guest a PROXY v1 line
--directoffWith --tcp: also open the port on the sandbox host and print that address
bash
pt sandbox expose sbx_01J... 8080   # https://8080-01j....sbx.example.com/?t=...

pt sandbox network ​

FlagDefaultDescription
--allow-outnoneComma-separated IPs, CIDRs, hostnames or *. wildcards
--deny-outnoneComma-separated IPs or CIDRs; all means 0.0.0.0/0
--no-internetoffBlock all outbound; do not combine with --allow-out
--headernonehost:Name:Value header to inject; repeatable
--showoffPrint 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.

bash
pt sandbox network sbx_01J... --deny-out all --allow-out pypi.org,files.pythonhosted.org

pt sandbox snapshots and backups ​

text
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
CommandFlagDefaultDescription
restore--snapshotlatestSnapshot id
restore--no-waitoffReturn before running
restore--timeout120Seconds to wait
fork--namenoneName for the fork
clone--snapshotlatestSnapshot id

pt template ​

text
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.

FlagDefaultDescription
--imagenoneContainer image to build from
--repononehttp(s) git URL with a Dockerfile
--refnoneGit branch, tag or sha
--subdirnoneBuild context inside the repo
--dockerfilenonePath relative to the context
--tokennoneAccess token for a private repo; never stored
--entrypointnoneOverride, with --image only
--cpu1Default vCPUs for sandboxes
--ram512Default RAM in MB
--disk2Default disk in GB
--size-mb1024 image, 8192 repoMax rootfs size in MB
--regionnonePin the build and template to a region
--globaloffVisible to every organization; admin only
--waitoffWait until ready
--timeout1800Seconds to wait
bash
pt template build my-tpl --image ubuntu:24.04 --wait

pt template update ​

FlagDefaultDescription
--versionunchangedVersion string
--cpuunchangedDefault vCPUs
--ramunchangedDefault RAM in MB
--diskunchangedDefault disk in GB
--entrypointunchangedEntrypoint

pt vol ​

pt volume is an alias. Every pt vol command needs object storage on the control plane and fails with 503 without it.

text
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 outside

Names are up to 63 letters, digits, ., _ or -, starting with a letter or digit. Names starting with vol_ or tmp-, and limits, are reserved.

CommandFlagDefaultDescription
create--modedrivedrive syncs on its own; git needs commit and pull
list--limitallMax rows
list--alloffInclude temporary volumes
delete--missing-okoffSucceed when the volume does not exist
ls-r, --recursiveoffWalk the whole tree
ls, cat, stat--atheadRead at a commit id or tag
cp-r, --recursiveoffCopy directories
cp--overwriteoffReplace existing files on the volume
cp--atheadRead the source at a commit id or tag
mv--overwriteoffReplace an existing destination
rm-r, --recursiveoffRemove a directory and its contents
detach--no-commitoffDiscard pending changes instead of committing
publish, wait--mountevery mountOnly this mount (vm_ id)
wait--timeout60Seconds
log--limit20Max commits
log--beforenonePage cursor from a previous listing
log--sideoffInclude commits merged rather than landed on head
diff--pathvolume rootOnly changes under this directory
tag-d, --deleteoffRemove the tag; no tag argument lists tags
fork--atheadCommit id or tag to fork from
bash
pt vol cp -r ./dataset data:/dataset         # creates the volume `data`
pt sandbox create -v data:/data -v ref:/ref:ro

pt secret ​

Every command takes the secret's name or sec_ id. Values come from a hidden prompt or stdin, never a flag.

text
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>
bash
printf %s "$KEY" | pt secret create search-api --allow api.example.com --header authorization
CommandFlagDefaultDescription
create, update--allowrequired on createExact hostnames the value may be sent to; no wildcards
create, update--headerrequired on createRequest headers the value is written into
create, update--descriptionnoneWhat the credential is for
create, update--on-echoblockblock or redact when a response echoes the value
update--nameunchangedRename
list--limit50Page size; up to 200
list--cursornoneCursor from a previous page
bind--sandboxrequiredSandbox id
bind--aliasnoneKey the binding is addressed by; not an env var in the guest

On update, --allow and --header replace the stored list.

pt key ​

text
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
FlagDefaultDescription
--namerequiredKey name
--scope*Comma-separated scopes, such as sandboxes:read,templates:*
--expires-inneverDays until the key expires

WARNING

pt key create prints the key once. Save it before you close the terminal.

pt webhook ​

text
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>
CommandFlagDefaultDescription
create--urlrequiredEndpoint URL
create--events*Comma-separated events
update--enable, --disableunchangedResume or pause delivery
update--url, --eventsunchangedNew endpoint or events
deliveries--statusallpending, success, failed or dead_letter

pt audit ​

text
pt audit list | ls [flags]                # organization owners and admins only
FlagDefaultDescription
--actionallAction prefix, such as sandbox.
--actorallActor user id
--statusallok, denied or error
--limit50Max rows; up to 200

pt mcp ​

text
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
CommandFlagDefaultDescription
start--permissionsread,exec,writeTool groups to register; manage is opt-in. Env PT_MCP_PERMISSIONS
start--readonlyoffRegister read tools only
start--sandboxnonePin default-sandbox tools to this id. Env PT_MCP_SANDBOX
start--confine-sandboxnoneReject every other sandbox id. Env PT_MCP_CONFINE_SANDBOX
start--allow-public-exposeoffPermit public preview URLs. Env PT_MCP_ALLOW_PUBLIC_EXPOSE
init--globalproject .mcp.jsonclaude: register for the user
init--project~/.cursor/mcp.jsoncursor: write ./.cursor/mcp.json
config--with-tokenoffEmbed 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.

text
pt api [METHOD] <path> [flags]
FlagDefaultDescription
-X, --methodGET, or POST with --dataHTTP method
-d, --datanoneJSON body: literal, @file, or - for stdin
--querynoneQuery string, such as state=running&limit=20
bash
pt api POST /v1/sandboxes -d '{"template":"pt-base","cpu":2}'

See also: Guides · HTTP API · AI agents and MCP