Installation & updates
--no-update.
Global flags
These flags work with any command:--json is enabled automatically whenever output is piped or redirected, so scripts get JSONL without passing the flag.
Wherever a command takes a box ID, two aliases also work: current (the last box created in this shell) and self (the box you are running inside, when using the CLI from within a box).
Shell completion
box completions <shell> prints a completion script for bash, zsh, fish, or PowerShell. It completes subcommands and your live box IDs (plus current and self), with a 15 second cache so repeated tabs are instant.
Authentication
box onboard
Sets this machine up: signs you in through the browser, saves your token, then asks only for what your account still needs: the third-party safety answer, and plan checkout.
--google / --email answer that question up front. See Quickstart.
Safe to re-run. On a reinstall or a second machine it recognises the account you already have and skips each step that account has finished:
The answer is recorded on the environment when you give it, so both answers are remembered, not just “a platform of mine”. A plan is never treated as an answer, since dashboard checkout does not ask the question.
See Reinstalling, or a second machine.
box login [key]
Sign in with an existing API token, or start a browser sign-in flow if no key is given. GitHub is the default; Google and email also work.
--key-stdin reads the token from standard input instead of the command line, keeping it out of the process arguments that any other user on the host can read. Prefer it for non-interactive sign-in; see Use in Code. Passing the token positionally still works. The two are mutually exclusive. --key-stdin needs a box CLI newer than 0.1.211.
Without a key the CLI prints a URL, opens it, and waits up to six minutes for you to finish in the browser. --email sends a code to that address; omit the address and the browser page asks for it. Disposable email domains are refused.
Every method lands on the same account only if it is already connected to it. A method you have never used before starts a new account, even when the email matches; add connections from Dashboard > Account instead. See Quickstart.
Create production API keys with box api-key create or from the dashboard API Keys page. See API Keys for key management and Use in Docker for Docker and hosted-worker setup.
box api-key
Create and manage API keys for SDKs, CI, and other projects.
--ttl defaults to 90d and cannot exceed 365d. --box and --env are repeatable. --preset is read-only, full-box, ci, or admin. --actions and --preset cannot be combined.
Secrets are shown once at create and rotate and can never be retrieved again. list shows prefix, last four, scope, expiry, 30-day request total, and live resource count. usage still works after revoke. Revoke does not delete Boxes or Agents.
Create calls the dedicated POST /api/box/v1/api-keys/scoped endpoint and requires a browser session or an admin-scoped token. During rollout it may be temporarily unavailable; api-key list remains available. Rotate and revoke stay session-gated. See API Keys.
box webhook
Register account-wide lifecycle webhooks for automation.
box.ready, box.error, box.archived, and box.hydrated. Omit --event to subscribe to all four. The signing secret is shown only at create/rotate time. See Webhooks.
box logout
Sign out and clear the local token.
box status
Show API health, the signed-in account and plan, and the local config path.
Box lifecycle
box new
Create a new box.
--type picks the machine size: small runs at half rate, default at 1x, large at 2x, and xlarge at $0.20 per running hour. xlarge requires the effective $100 plan or higher and a bare-metal operator allocation. A fork inherits the source box’s size unless you pass --type, and box resume --type moves an existing box between sizes. See Machine Capabilities for the specs of each size and Billing & Limits for what they cost.
On the free trial,
--no-auto-stop and a --ttl above 2 hours are refused, and --type large is not available. See On the free trial.--env sets per-box environment variables on top of your dashboard secrets; per-box values win on name conflicts. See Environments.
--environment <name> picks which environment the box starts from: its repositories, secrets, and credentials. Omit it to use your default. An unknown name is rejected before the box is created, so a typo costs you nothing. Note that --environment and --env are unrelated: one picks a template, the other sets a variable on this one box. See Environments.
--no-env creates a box that receives none of the secrets attached to your account, and confines the box to itself so it can’t act on your account or other boxes. Use it for boxes you give to your own users. SSH, SCP, desktop, snapshots, and public URLs still work. See Environments. For more than the occasional box, prefer an environment marked safe for third parties, which applies the same protection to every box that uses it and survives forks and resumes.
--setup-file reads a local shell script (UTF-8, up to 64KB) and runs it on the box in the background after the box is ready, and it never delays ready. Watch the outcome as setupStatus (pending, running, done, failed) and setupError in box info. It combines with --from.
--personal bills you even when an org wallet is active. Day to day, run box org switch once and every later box new follows it. Override one create with box --org <org> new or box --org personal new. See box org.
--from <name> starts the box from a named snapshot you saved with box snapshot <id> <name>, so the stack is already installed. It does not carry the source box’s environment: pass --environment, or take your default. Note this is unrelated to box env set-file --from, which reads a local file. See Snapshots & Copies.
Returns the box ID, IP, and initial state.
For a long uninterrupted workflow, disable auto-stop:
box list
List your up/running boxes with their current state and ID. By default, box list is equivalent to box list --filter r.
--filter with state group letters:
Combine letters, for example
box list --filter sr lists stopped and up/running boxes. Use --all to include every state.
box extend <id>
Change the auto-stop timer for an existing Box.
--hours or --ttl for a timed extension. Use --no-auto-stop when the Box should keep running until you stop it yourself.
box info <id>
Get details for a specific box: state, IP, desktop availability, TTL remaining.
box stop <id>
Pause a running box. Creates a snapshot then stops billing. The box enters archiving → archived.
Snapshotting takes a moment. The box is not yet stopped when the command returns; poll
box info to confirm archived state.--force stops the box anyway and permanently loses everything written since the last successful snapshot. It is irreversible, so only use it after a stop has already failed.
box delete <id>
Permanently delete a box and its snapshots. The box is force-stopped without a final snapshot, then every snapshot chain it exclusively owns is deleted from storage.
--yes to skip the prompt; in scripts (--json, or no terminal attached) --yes is required and the command refuses without it rather than hanging.
Deletion is not instant: the machine has to be torn down before its data can be removed. The box disappears from box list right away, the command prints the deletion operation id (bdop_…), and then follows that operation until it finishes.
What survives. Snapshot data shared with something else is kept, because deleting it would break whatever else reads it:
- a box you forked from this one, or a resume of it, restores from the same physical snapshot objects
- a named snapshot saved from this box (
box snapshot <id> <name>) keeps its data; remove it withbox snapshot rm <name>if you want those bytes gone too
box deletion status <operation-id>
Check back on a deletion you started earlier, here or through the API. Statuses are pending, processing, blocked and completed. blocked is not a dead end: an attempt hit something it could not finish yet, most often a snapshot another box still reads, and it is retried automatically with backoff.
box resume <id>
Resume a stopped box from its last snapshot. The box is usable in a few seconds regardless of data size; remaining file content streams in the background. See Snapshots.
box stop).
Use -e/--env to set per-box environment variables on resume, with the same semantics as box new -e: repeat for multiple values, per-box values override dashboard variables with the same name, and the set you pass replaces the box’s current per-box variables.
Use --environment <name> to move the box onto a different environment as it resumes. Omit it and the box keeps the environment version it already had; a resume never silently moves a box to a newer version. An unknown name is rejected before anything changes. If the new environment withholds something the box currently holds, that secret is scrubbed off the disk during the resume and does not come back. See Environments.
Use --no-env to resume a stopped box after dropping your account secrets and scrubbing owner secrets inherited from the snapshot. This is one-way: the box stays no-env afterward.
Use --type to resume onto a different machine size (small, default, large, or xlarge). Omit it to keep the box’s current size. Shrinking is refused if the box holds more data than the smaller machine can take, and the box is left untouched. See Machine Capabilities.
Use --ttl <seconds> to set the resumed box’s lifetime, or --no-auto-stop to switch auto-stop off. Omit both and the box keeps the setting it already had, so a resume never quietly shortens or extends it.
Resume behaves like a server reboot: systemd services you enabled start again automatically. Processes you ran by hand (dev servers, background jobs, tunnels, desktop sessions) do not survive; restart them, or make them a systemd service.
For xlarge, resume with the same explicit bare-metal operator pin. The effective billing plan must be $100 or higher.
box fork <id>
Clone a box from its latest snapshot into a new independent box.
--type to give the fork a different machine size from its source. The source box is never modified.
Use --ttl <seconds> or --no-auto-stop to set the fork’s lifetime. A fork does not inherit its source’s: it defaults to 1 hour, so forking a box with auto-stop switched off does not silently produce another box nothing will stop.
Use --environment <name> to point the fork at a different environment. Omit it and the fork inherits exactly the environment version its source is on, so a fork never picks up configuration the source never had. The source box is never modified either way. See Environments.
Use --no-env to fork into a no-env box. A fork of a no-env source is always no-env.
Use -e/--env to set per-box environment variables on the fork, with the same semantics as box new -e. The set you pass replaces the per-box variables the fork would inherit from the source box; omit it to inherit them unchanged.
box events <id>
Read the agent’s work on a Box: prompts and responses. By default it streams every conversation on the box; each event carries a conversationId (visible with --json). Add --follow to keep polling for new events, or --convo to watch a single conversation.
These are the agent’s conversations, not the Box’s lifecycle. A Box that has never been prompted returns an empty list even though it started, stopped and resumed. For lifecycle state use
box info.box conversations [id]
List a box’s conversations, most recently prompted first: id, last activity, prompt count, whether a turn is running, the last harness and model, and a preview of the last prompt. Alias box convos.
box current is the conversation a bare box prompt from anywhere would continue (the box’s most recently prompted one); this shell is the one a bare box prompt from this shell continues. Copy an id into box prompt --resume <id>, box steer --convo <id>, box events --convo <id> or box interrupt --convo <id>.
box steer <id> <message>
Send a message to a turn that is already running, without stopping it. The agent takes it into account and keeps what it was doing. This is the third option next to box prompt (which waits for the turn to end) and box interrupt (which throws it away).
--convo and it steers the conversation this shell last prompted, the same one a bare box prompt continues. Steering a conversation with nothing in flight is refused with no_running_turn; use box prompt for that.
Claude Code, Codex, pi and Prime Agent take the message into the running turn natively, so nothing is interrupted; if a harness accepts the message but its turn ends without acting on it, Box continues it immediately as its own turn on the same session, so a steer is always acted on. OpenCode has no mid-turn primitive, so Box stops that turn and immediately continues the same conversation with your instruction, keeping all of its memory. The JSON output’s native field says which path ran, and the steer appears in box events as a steer event carrying the settled mode (native, native-continued, fallback, late). See Integrated agents.
box interrupt <id>
Interrupt running agent work in a Box. By default it stops every conversation on the box; scope it to one with --convo and the others keep running.
Access
box ssh <id> [command]
SSH into a box, or run a command non-interactively. Manages the key at ~/.ssh/ascii_box_ed25519 automatically.
box exec <id> [command...]
Run a command in a box over the Box API, without an SSH session or key setup. box exec exits with the remote command’s exit code, so it drops straight into scripts and CI.
Wait for the box to reach
ready before running box exec; earlier calls are refused with a retryable box_starting error.
For anything that may run longer than the 600 second synchronous cap, detach and poll instead:
--detach starts the command in the background and answers with a JSON object; take processId from it, as above. Remember that --json turns itself on whenever output is piped or captured, so inside a script you always get JSON here even without the flag. Output goes to ~/.ascii/processes/<pid>.log on the box. --status <pid> prints whether it is still running, the exit code once finished, and a tail of stdout/stderr. If the box’s agent restarts (stop/resume), the status degrades to lost but the log files stay on disk. See Long-Running Tasks.
box env
Manage the templates new boxes start from: repositories, secrets, and which of your credentials a box may use. Every change mints a new immutable version. Running boxes stay on the version they started with until box env upgrade. See Environments.
box env set <name>
Change what the environment injects.
Contents
set-file reads contents from --from <local-file>, or from stdin when --from is omitted. Paths are relative to the box work directory (/home/user).
box env upgrade <name>
Move this environment’s boxes onto its latest version. Live boxes get the new configuration pushed immediately, with any secret the new version withholds scrubbed off the machine first. Stopped boxes pick it up when they resume. Nothing upgrades on its own.
box host <id> <port>
Expose a running service inside a Box on a stable HTTPS URL without opening an interactive SSH session.
box scp
Copy files to/from a box. Use bx_<id>:/path as the remote address.
On macOS and Linux, recursive copies to a new directory stream one archive over SSH when
tar is available on both ends, avoiding per-file network round trips. The new directory becomes visible only after a complete transfer. Files, existing-directory merges, and unsupported trees use the installed OpenSSH scp.
box forward <id>
Forward one TCP port from a box to your local machine.
--reverse: reach your machine from the box
--reverse sends the tunnel the other way. A port on your machine becomes
reachable inside the box at 127.0.0.1:<remote>, so an agent in the box can
call an MCP server, a local model, or a webhook receiver that never leaves your
laptop.
Both directions run in the foreground until Ctrl+C. If the tunnel drops because
the box moved (
box stop then box resume brings it back on a new IP), the CLI
refetches the connection and redials up to five times before giving up. A
failure at startup, such as the port already being taken on the far side, is
reported straight away instead of being retried.
Nothing is exposed publicly by either direction: the tunnel rides the same SSH
session as box ssh, and the box side listens on loopback only. For a public
HTTPS URL for a port inside the box, use box host.
box desktop <id>
Open the box’s desktop streaming URL in your browser.
--vnc for
a VNC stream that tunnels over plain HTTPS and is far more tolerant of poor
connections. The viewer also offers a one-click switch to VNC if the default
stream struggles.
The first
--vnc on a box takes a few seconds while it prepares the VNC
stack (the CLI shows a spinner); subsequent opens are instant.box browser <id>
Return a browser-only stream from the same running Box.
--profile lets you keep several signed-in Chrome profiles on one box and pick
one per stream. The directory is created and given to the browser user for you.
It must be an absolute path outside system directories, and it must not live
under /home/user, which the browser sandbox masks with an empty directory.
Because the profile lives on the box filesystem, snapshots and forks carry it
along. Switching profiles restarts Chrome and leaves the stream up. Boxes
created before this feature return chrome_profile_unsupported; recreate the
box on a current image to use the flag.
The stream is 1920x1080 at 60 fps. It shows Chrome with tabs and an address bar, uses an isolated streaming process and identity, does not expose the desktop clipboard bridge, does not create a separate Box, and cannot fall back to the desktop or VNC. Window-close chrome is hidden. Mute, reconnect, and fullscreen stay on the viewer. Closing the last tab opens a new tab and leaves the stream up. Running the command again leaves the live stream up. Opening the new URL in a second tab replaces the first tab and keeps the same Chrome session. New boxes receive the browser-view files at provision. Treat the URL as a secret.
This is display and input confinement, not isolation from the Box itself. Code already running in the Box, especially code with root access, can inspect or modify the browser process and its data.
Snapshots
box snapshots [id]
List snapshots across your boxes, or for one box.
box snapshot <id> <name>
Save a box’s disk under a name, so box new --from <name> can deploy it as many times as you like. Reuse a name to replace it.
ready. You can keep up to 10 named snapshots. See Snapshots & Copies.
box snapshot latest|tree|pull
Inspect or download a snapshot. Works while the box is stopped.
pull writes home_user/ (your /home/user) and docker/ (named volumes). See Snapshots.
box snapshot delete <snapshotId>
Permanently delete one ordinary filesystem snapshot. Refused with 409 while a later incremental snapshot or an active restore still depends on it. Named snapshots are removed by name with box snapshot rm <name> instead.
AI agents
box prompt [id]
Send a natural-language prompt to an AI agent running inside the box. Omit the ID to target the current box of your shell (the last box new there), or the box you are running inside. Omit --provider to use the default harness and model selected on the Agents page of the dashboard.
Run
box prompt --help to fetch the current provider, model, and reasoning-effort list from the Box API.
Conversations (running agents in parallel)
A box runs many conversations at once, each with its own history, so one box can drive several independent agent tasks (different features, different users, a demo) in parallel rather than queuing them one behind another.--newstarts a fresh conversation and makes it this shell’s current one. Every prompt prints its conversation id right underqueued:(conversation: <id>, with(new)when--newcreated it) and carries it asconversationIdin--json.--resume <id>continues a specific conversation;--resume currenttargets this shell’s current one.- No flag continues this shell’s current conversation. The “current conversation” is scoped to your shell, exactly like the
currentbox id, so two shells (or two people) prompting the same box each keep their own thread and don’t disturb each other. box conversationslists every conversation on the box with its id, prompt count, running state and last prompt, so a thread from another shell or another day can be resumed by id.- Conversations run in parallel up to a per-box limit that scales with the box’s memory; beyond it, further prompts queue and start as turns finish. Continue a conversation on a different
--providerand its history carries across the switch.
Account
box limits
Show remaining machine starts, compute time, and credits. Create, fork, and resume each count as one start.
--json is the same payload the API returns, including starts.minute.remaining, creditBalanceHours, and packBalanceDollars.
box usage <id>
Show the machine time one box has consumed and what it costs, over its whole life or a window. Works on running and stopped boxes; a running box’s figure keeps growing until it stops, and running in the JSON says whether it still is (false once stopped, or while a refused stop holds the meter paused).
Human output prints billable machine time (the box type’s multiplier applied), the cost at list price, and the window.
--json is the API payload: seconds, dollars, secondsPerDollar, boxType, billingMultiplier, since, until and running. See Per-box usage.
box dashboard
Open your web dashboard in the browser, already signed in. The link carries a single-use code that expires in 15 minutes and cannot call the API (never your Box token), so it is safe in a terminal, a log, or a message. If you signed in with a service API key, the link opens the dashboard signed out: creating, rotating, or revoking API keys requires the browser sign-in flow.
box org
Show, switch, create, transfer, or delete the organization wallet that new boxes bill to. One shared plan and balance. box org switch is sticky; --org (or BOX_ORG) overrides it for one command.
box org transfer and box org delete target the invocation scope (--org / BOX_ORG if set, otherwise the sticky wallet). Deleting an org with --org does not clear a different sticky wallet.
box data-retention
Show or enable zero data retention, which queues every archived box for permanent deletion.
box login without a key), not an API key. See Data retention and deletion.
box self-update
Check for and install the latest CLI release on your current channel.
~/.config/ascii/box/config.json. box status prints its path along with the resolved API URL, account and plan.