> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boat.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI Reference

> Complete reference for all Boat CLI commands and global flags.

## Installation & updates

```bash theme={null}
# Install (run once)
curl -fsSL https://boat.dev/install | sh

# Check for and apply updates
boat self-update
```

The CLI auto-checks for updates on each run. Suppress with `--no-update`.

***

## Global flags

These flags work with any command:

| Flag                 | Description                                                                                                            |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `--api-url <URL>`    | Override the API endpoint (env: `BOAT_API_URL`)                                                                        |
| `--org <ID_OR_NAME>` | Run this command against an organization wallet (env: `BOAT_ORG`). Does not change the sticky `boat org switch` scope. |
| `--json`             | Output machine-readable JSON instead of human text                                                                     |
| `--no-update`        | Skip the automatic update check                                                                                        |

`--json` is enabled automatically whenever output is piped or redirected, so scripts get JSONL without passing the flag.

Wherever a command takes a sandbox ID, two aliases also work: `current` (the last sandbox created in this shell) and `self` (the sandbox you are running inside, when using the CLI from within a sandbox).

***

## Shell completion

`boat completions <shell>` prints a completion script for bash, zsh, fish, or PowerShell. It completes subcommands and your live sandbox IDs (plus `current` and `self`), with a 15 second cache so repeated tabs are instant.

```bash theme={null}
# bash: add to ~/.bashrc
eval "$(boat completions bash)"

# zsh: add to ~/.zshrc, after compinit
eval "$(boat completions zsh)"

# fish
boat completions fish > ~/.config/fish/completions/sandbox.fish

# PowerShell: add to $PROFILE
boat completions powershell | Out-String | Invoke-Expression
```

***

## Authentication

### `boat 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.

```bash theme={null}
boat onboard
boat onboard --google
boat onboard --email you@example.com
```

It asks which sign-in method to use (GitHub, Google, or a 6-digit code emailed to you), and `--google` / `--email` answer that question up front. See [Quickstart](/quickstart#sign-in-methods).

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:

| Your account already has             | What `boat onboard` does                                                                        |
| ------------------------------------ | ----------------------------------------------------------------------------------------------- |
| An active plan                       | Prints your plan and usage. **No Stripe Checkout**, no second subscription.                     |
| A plan with depleted usage           | Points you at the billing dashboard to top up or change tier, instead of starting another plan. |
| Answered what your sandboxes are for | Does not ask that question again, whichever way you answered.                                   |
| A past subscription                  | Offers checkout without promising a 7-day trial, which Stripe grants once per customer.         |

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](/quickstart#reinstalling-or-a-second-machine).

### `boat 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.

```bash theme={null}
boat login
boat login --google
boat login --email you@example.com
boat login --key-stdin <<< "$BOAT_API_KEY"
boat login boat_abc123...
```

`--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](/use-in-code#authenticate). Passing the token positionally still works. The two are mutually exclusive. `--key-stdin` needs a Boat 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](https://boat.dev/dashboard?tab=account) instead. See [Quickstart](/quickstart#sign-in-methods).

Create production API keys with `boat api-key create` or from the dashboard API Keys page. See [API Keys](/api-keys) for key management and [Use in Docker](/use-in-production) for Docker and hosted-worker setup.

### `boat api-key`

Create and manage API keys for SDKs, CI, and other projects.

```bash theme={null}
boat api-key create my-project --ttl 90d --preset ci
boat api-key create my-project --ttl 7d --sandbox bx_123 --actions sandbox.read,exec
boat api-key list                # scope, expiry, last used
boat api-key list --all          # also platform-managed per-sandbox machine keys
boat api-key usage <id>          # 30-day requests and live resource count
boat api-key usage <id> --verbose  # plus sandboxes/Agents split and created list
boat api-key rotate <id>         # old secret stops working, new one shown once
boat api-key revoke <id>
```

`--ttl` defaults to 90d and cannot exceed 365d. `--sandbox` and `--env` are repeatable. `--preset` is `read-only`, `full-sandbox`, `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 sandboxes or Agents.

Create calls the dedicated `POST /api/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](/api-keys).

### `boat webhook`

Register account-wide lifecycle webhooks for automation.

```bash theme={null}
boat webhook create https://example.com/hooks/boat --event ready --event error
boat webhook list
boat webhook rotate <id>
boat webhook remove <id>
```

Supported events are `sandbox.ready`, `sandbox.error`, `sandbox.archived`, and `sandbox.hydrated`. Omit `--event` to subscribe to all four. The signing secret is shown only at create/rotate time. See [Webhooks](/webhooks).

### `boat logout`

Sign out and clear the local token.

```bash theme={null}
boat logout
```

### `boat status`

Show API health, the signed-in account and plan, and the local config path.

```bash theme={null}
boat status
```

***

## Sandbox lifecycle

### `boat new`

Create a new sandbox.

```bash theme={null}
boat new [--ttl SECONDS]
boat new --type small
boat new --type large
boat new --type xlarge
boat new --no-auto-stop
boat new --env KEY=VALUE --env OTHER=VALUE
boat new --no-env
boat new --setup-file ./setup.sh
boat new --environment users
boat new --from web-stack
boat --org acme new
boat org switch acme
boat new
boat new --personal
```

| Flag             | Default               | Maximum                       |
| ---------------- | --------------------- | ----------------------------- |
| `--type`         | `default`             | `small`, `large`, or `xlarge` |
| `--ttl`          | `3600` (1 hour)       | `2592000` (30 days)           |
| `--no-auto-stop` | off                   | no automatic stop             |
| `--env`, `-e`    | none                  | 100 variables, 64KB total     |
| `--environment`  | your default (`base`) | one named environment         |
| `--no-env`       | off                   | none                          |
| `--setup-file`   | none                  | 64KB script file              |
| `--from`         | none                  | one named snapshot            |
| `--personal`     | off                   | bills you, not the active org |

`--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 sandbox's size unless you pass `--type`, and `boat resume --type` moves an existing sandbox between sizes. See [Machine Capabilities](/machines) for the specs of each size and [Billing & Limits](/billing) for what they cost.

<Note>
  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](/billing#on-the-free-trial).
</Note>

`--env` sets per-sandbox environment variables on top of your dashboard secrets; per-sandbox values win on name conflicts. See [Environments](/environments).

`--environment <name>` picks which environment the sandbox starts from: its repositories, secrets, and credentials. Omit it to use your default. An unknown name is rejected before the sandbox 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 sandbox. See [Environments](/environments).

`--no-env` creates a sandbox that receives none of the secrets attached to your account, and confines the sandbox to itself so it can't act on your account or other sandboxes. Use it for sandboxes you give to your own users. SSH, SCP, desktop, snapshots, and public URLs still work. See [Environments](/environments). For more than the occasional sandbox, prefer an environment marked safe for third parties, which applies the same protection to every sandbox 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 sandbox in the background after the sandbox is ready, and it never delays `ready`. Watch the outcome as `setupStatus` (`pending`, `running`, `done`, `failed`) and `setupError` in `boat info`. It combines with `--from`.

`--personal` bills you even when an org wallet is active. Day to day, run `boat org switch` once and every later `boat new` follows it. Override one create with `boat --org <org> new` or `boat --org personal new`. See [`boat org`](#sandbox-org).

`--from <name>` starts the sandbox from a named snapshot you saved with `boat snapshot <id> <name>`, so the stack is already installed. It does not carry the source sandbox's environment: pass `--environment`, or take your default. Note this is unrelated to `boat env set-file --from`, which reads a local file. See [Snapshots & Copies](/snapshots).

Returns the sandbox ID, IP, and initial state.

For a long uninterrupted workflow, disable auto-stop:

```bash theme={null}
boat new --no-auto-stop
```

See [Long-Running Tasks](/long-running-tasks).

### `boat list`

List your up/running sandboxes with their current state and ID. By default, `boat list` is equivalent to `boat list --filter r`.

```bash theme={null}
boat list
boat list --filter s
boat list --filter sr
boat list --all
```

Use `--filter` with state group letters:

| Filter | Includes                                                    |
| ------ | ----------------------------------------------------------- |
| `r`    | Up/running sandboxes: `ready`, `cloning`, `idle`, `running` |
| `s`    | Stopped sandboxes: `stopped`                                |
| `p`    | Pending sandboxes: `init`, `provisioning`                   |
| `t`    | Stopping sandboxes: `stopping`                              |
| `e`    | Error sandboxes: `error`                                    |

Combine letters, for example `boat list --filter sr` lists stopped and up/running sandboxes. Use `--all` to include every state.

### `boat extend <id>`

Change the auto-stop timer for an existing sandbox.

```bash theme={null}
boat extend bx_f7k2q9hd --hours 12
boat extend bx_f7k2q9hd --ttl 2592000
boat extend bx_f7k2q9hd --no-auto-stop
```

Use `--hours` or `--ttl` for a timed extension. Use `--no-auto-stop` when the sandbox should keep running until you stop it yourself.

### `boat info <id>`

Get details for a specific sandbox: state, IP, desktop availability, TTL remaining.

```bash theme={null}
boat info bx_f7k2q9hd
```

### `boat stop <id>`

Pause a running sandbox. Creates a snapshot then stops billing. The sandbox enters `archiving` → `archived`.

```bash theme={null}
boat stop bx_f7k2q9hd
boat stop bx_f7k2q9hd --force
```

<Note>Snapshotting takes a moment. The sandbox is not yet stopped when the command returns; poll `boat info` to confirm `archived` state.</Note>

**If a stop is refused.** Stopping saves the disk first. If that save is failing, we refuse the stop and leave the sandbox running rather than discard your work, retry automatically, and email you. **You are not billed for the time your sandbox spends in that state**: the meter pauses by itself from the first failed attempt. See [When a stop is refused](/snapshots#when-a-stop-is-refused).

`--force` stops the sandbox anyway and permanently loses everything written since the last successful snapshot. It is irreversible, so only use it after a stop has already failed.

<Warning>`--force` discards unsaved work. Check `boat info` for the last snapshot time first, so you know exactly what you are giving up.</Warning>

### `boat delete <id>`

Permanently delete a sandbox **and its snapshots**. The sandbox is force-stopped without a final snapshot, then every snapshot chain it exclusively owns is deleted from storage.

```bash theme={null}
boat delete bx_f7k2q9hd
boat delete bx_f7k2q9hd --yes
boat delete self --yes              # from inside a sandbox
```

<Warning>
  `boat delete` destroys data. A deleted sandbox cannot be resumed, forked or recovered, and there is no deleted-sandboxes list to restore from.

  **If you want the data usable later, `boat stop` it instead of deleting it.** A stopped sandbox is free, keeps its disk, and `boat resume` brings it back where you left it.
</Warning>

The command asks for confirmation. Pass `--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 sandbox disappears from `boat 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 sandbox you **forked** from this one, or a **resume** of it, restores from the same physical snapshot objects
* a **named snapshot** saved from this sandbox (`boat snapshot <id> <name>`) keeps its data; remove it with `boat snapshot rm <name>` if you want those bytes gone too

Storage is released once the last sandbox or named snapshot using a chain is gone.

### `boat 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 sandbox still reads, and it is retried automatically with backoff.

```bash theme={null}
boat deletion status bdop_0123456789abcdef0123456789abcdef
```

See [Data retention and deletion](/data-retention).

### `boat resume <id>`

Resume a stopped sandbox from its last snapshot. The CLI waits for a ready state and a successful no-op command before printing its final `ready` event. This checks the command-execution restore gate; it does not wait for background hydration or setup scripts. The wait is bounded to 30 minutes and fails on terminal sandbox states or non-retryable API errors. Older installed Box and Boat binaries need an updated CLI to get this readiness check. See [Snapshots](/snapshots).

```bash theme={null}
boat resume bx_f7k2q9hd
boat resume --no-env bx_f7k2q9hd
boat resume --type large bx_f7k2q9hd
boat resume -e KEY=VALUE bx_f7k2q9hd
boat resume --ttl 7200 bx_f7k2q9hd
boat resume --no-auto-stop bx_f7k2q9hd
```

Requires the sandbox to have a completed snapshot (i.e. it was stopped cleanly via `boat stop`).

Use `-e`/`--env` to set per-sandbox environment variables on resume, with the same semantics as `boat new -e`: repeat for multiple values, per-sandbox values override dashboard variables with the same name, and the set you pass **replaces** the sandbox's current per-sandbox variables.

Use `--environment <name>` to move the sandbox onto a different environment as it resumes. Omit it and the sandbox keeps the environment version it already had; a resume never silently moves a sandbox to a newer version. An unknown name is rejected before anything changes. If the new environment withholds something the sandbox currently holds, that secret is scrubbed off the disk during the resume and does not come back. See [Environments](/environments).

Use `--no-env` to resume a stopped sandbox after dropping your account secrets and scrubbing owner secrets inherited from the snapshot. This is one-way: the sandbox stays no-env afterward.

Use `--type` to resume onto a different machine size (`small`, `default`, `large`, or `xlarge`). Omit it to keep the sandbox's current size. Shrinking is refused if the sandbox holds more data than the smaller machine can take, and the sandbox is left untouched. See [Machine Capabilities](/machines).

Use `--ttl <seconds>` to set the resumed sandbox's lifetime, or `--no-auto-stop` to switch auto-stop off. Omit both and the sandbox 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.

### `boat fork <id>`

Clone a sandbox from its latest snapshot into a new independent sandbox.

```bash theme={null}
boat fork bx_f7k2q9hd
boat fork --no-env bx_f7k2q9hd
boat fork --type small bx_f7k2q9hd
boat fork -e TENANT_ID=acme bx_f7k2q9hd
boat fork --ttl 600 bx_f7k2q9hd
boat fork --no-auto-stop bx_f7k2q9hd
```

Returns the new sandbox ID asynchronously (HTTP 202). Requires the source sandbox to have a completed snapshot.

Forking copies the snapshotted filesystem into a new Sandbox; enabled systemd services start automatically, like on resume. Processes the source sandbox ran by hand are not forked; start them again in the fork if needed.

Use `--type` to give the fork a different machine size from its source. The source sandbox 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 sandbox with auto-stop switched off does not silently produce another sandbox 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 sandbox is never modified either way. See [Environments](/environments).

Use `--no-env` to fork into a no-env sandbox. A fork of a no-env source is always no-env.

Use `-e`/`--env` to set per-sandbox environment variables on the fork, with the same semantics as `boat new -e`. The set you pass **replaces** the per-sandbox variables the fork would inherit from the source sandbox; omit it to inherit them unchanged.

### `boat events <id>`

Read the agent's work on a Sandbox: prompts and responses. By default it streams **every conversation** on the sandbox; each event carries a `conversationId` (visible with `--json`). Add `--follow` to keep polling for new events, or `--convo` to watch a single conversation.

<Note>
  These are the agent's conversations, not the sandbox's lifecycle. A Sandbox that has never been prompted returns an empty list even though it started, stopped and resumed. For lifecycle state use `boat info`.
</Note>

```bash theme={null}
boat events bx_f7k2q9hd
boat events bx_f7k2q9hd --follow
boat events bx_f7k2q9hd --convo <conversationId>          # just one conversation
boat events bx_f7k2q9hd --convo current                    # this shell's current conversation
```

| Flag           | Description                                                                                                                |
| -------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `--follow`     | Continue polling and printing new events until interrupted                                                                 |
| `--convo <id>` | Only stream this conversation (or `current`). Repeat for several. Omit to stream all conversations. Alias `--conversation` |

### `boat conversations [id]`

List a sandbox'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 `sandbox convos`.

```bash theme={null}
boat conversations
sandbox convos bx_f7k2q9hd
boat conversations bx_f7k2q9hd --json      # one JSON line per conversation
```

```
ID                                    When         Prompts  Running  Harness/model      Last prompt
8f1c2b7a-3d4e-4f5a-9b0c-1d2e3f4a5b6c  just now     1        yes      pi/claude-sonnet-5 Investigate the flaky CI job  [sandbox current, this shell]
2c0d9e4b-7a1f-4c3e-8b5d-6e7f8a9b0c1d  4 mins ago   2        no       claude             Now add tests
```

Two markers can follow the last prompt: `sandbox current` is the conversation a bare `boat prompt` from anywhere would continue (the sandbox's most recently prompted one); `this shell` is the one a bare `boat prompt` from this shell continues. Copy an id into `boat prompt --resume <id>`, `boat steer --convo <id>`, `boat events --convo <id>` or `boat interrupt --convo <id>`.

### `boat 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 `boat prompt` (which waits for the turn to end) and `boat interrupt` (which throws it away).

```bash theme={null}
boat steer "Skip the integration tests, the unit tests are enough"
boat steer bx_f7k2q9hd "Also update the changelog when you are done"
boat steer bx_f7k2q9hd --convo <conversationId> "Focus on the auth module first"
boat steer --convo current "Stop after the first failure"
```

Omit `--convo` and it steers the conversation this shell last prompted, the same one a bare `boat prompt` continues. Steering a conversation with nothing in flight is refused with `no_running_turn`; use `boat 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, Boat 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 sandbox 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 `boat events` as a `steer` event carrying the settled `mode` (`native`, `native-continued`, `fallback`, `late`). See [Integrated agents](/integrated-agents).

| Flag           | Description                                                                                                    |
| -------------- | -------------------------------------------------------------------------------------------------------------- |
| `--convo <id>` | Steer this conversation (or `current`). Omit to steer the one this shell last prompted. Alias `--conversation` |

### `boat interrupt <id>`

Interrupt running agent work in a sandbox. By default it stops **every conversation** on the sandbox; scope it to one with `--convo` and the others keep running.

```bash theme={null}
boat interrupt bx_f7k2q9hd
boat interrupt bx_f7k2q9hd --convo <conversationId>       # stop just one conversation
boat interrupt bx_f7k2q9hd --convo current
```

| Flag           | Description                                                                                                  |
| -------------- | ------------------------------------------------------------------------------------------------------------ |
| `--convo <id>` | Interrupt only this conversation (or `current`). Omit to interrupt the whole sandbox. Alias `--conversation` |

***

## Access

### `boat ssh <id> [command]`

SSH into a sandbox, or run a command non-interactively. Manages the key at `~/.ssh/ascii_box_ed25519` automatically (the same key file the box CLI used, so nothing to re-authorize).

```bash theme={null}
boat ssh bx_f7k2q9hd
boat ssh bx_f7k2q9hd "cd /home/user/my-repo && npm test"
boat ssh bx_f7k2q9hd -- bash -lc "cd /home/user/my-repo && npm test"
boat ssh bx_f7k2q9hd -- bash -s < ./setup.sh
```

### `boat exec <id> [command...]`

Run a command in a sandbox over the Boat API, without an SSH session or key setup. `boat exec` exits with the remote command's exit code, so it drops straight into scripts and CI.

```bash theme={null}
boat exec bx_f7k2q9hd "npm test"
boat exec bx_f7k2q9hd --cwd my-repo --timeout 120 -- npm run build
```

| Flag               | Default                | Description                                                              |
| ------------------ | ---------------------- | ------------------------------------------------------------------------ |
| `--cwd <dir>`      | sandbox work directory | Working directory, relative to the sandbox work directory (`/home/user`) |
| `--timeout <secs>` | `30`                   | Synchronous timeout, 1–600 seconds                                       |
| `--detach`         | off                    | Run in the background and print the process id                           |
| `--status <pid>`   | none                   | Show status and logs of a process started with `--detach`                |

Wait for the sandbox to reach `ready` before running `boat exec`; earlier calls are refused with a retryable `boat_starting` error.

For anything that may run longer than the 600 second synchronous cap, detach and poll instead:

```bash theme={null}
pid="$(boat exec bx_f7k2q9hd --detach "npm run build" | jq -r .processId)"
boat exec bx_f7k2q9hd --status "$pid"
```

`--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 sandbox. `--status <pid>` prints whether it is still running, the exit code once finished, and a tail of stdout/stderr. If the sandbox's agent restarts (stop/resume), the status degrades to `lost` but the log files stay on disk. See [Long-Running Tasks](/long-running-tasks).

### `boat env`

Manage the templates new sandboxes start from: repositories, secrets, and which of your credentials a sandbox may use. Every change mints a new immutable version. Running sandboxes stay on the version they started with until `boat env upgrade`. See [Environments](/environments).

<Warning>
  `--environment` and `--env` are different. `boat new --environment staging` picks which environment the sandbox uses. `boat new --env KEY=value` sets one variable on that single sandbox, on top of what the environment gives it.
</Warning>

```bash theme={null}
boat env list
boat env info base
boat env new staging
boat env rename staging prod
boat env default prod
boat env rm staging
```

| Command                             | Description                                                                                |
| ----------------------------------- | ------------------------------------------------------------------------------------------ |
| `boat env list`                     | Your environments, with the default marker, latest version, and live sandboxes per version |
| `boat env info <name>`              | One environment in full: toggles, variables, secret files, repos, versions                 |
| `boat env new <name>`               | Create an environment. Starts with everything injected                                     |
| `boat env rename <name> <new-name>` | Rename. Sandboxes stay pinned to their versions                                            |
| `boat env default <name>`           | Make it the environment new sandboxes use when none is named                               |
| `boat env rm <name>`                | Soft delete. Pinned sandboxes keep running; new sandboxes cannot use it                    |

#### `boat env set <name>`

Change what the environment injects.

```bash theme={null}
boat env set base --safe-for-third-parties true
boat env set base --github true --secrets false
```

| Flag                                     | Description                                                                      |
| ---------------------------------------- | -------------------------------------------------------------------------------- |
| `--safe-for-third-parties <true\|false>` | Master switch. `true` injects nothing of yours and overrides every flag below    |
| `--github <true\|false>`                 | Your GitHub access: token and repository clones                                  |
| `--secrets <true\|false>`                | Your environment variables and secret files                                      |
| `--sandbox-credentials <true\|false>`    | The in-sandbox Boat CLI credentials, so the sandbox can manage its own lifecycle |
| `--agents-credentials <true\|false>`     | Your agent provider logins (Claude, Codex, and so on)                            |

#### Contents

```bash theme={null}
boat env set-var base STRIPE_KEY=sk_live_123
boat env rm-var base STRIPE_KEY
boat env set-file base backend/.env --from ./local.env
cat ./local.env | boat env set-file base backend/.env
boat env rm-file base backend/.env
boat env add-repo base octocat/hello-world --branch develop
boat env rm-repo base octocat/hello-world
```

`set-file` reads contents from `--from <local-file>`, or from stdin when `--from` is omitted. Paths are relative to the sandbox work directory (`/home/user`).

#### `boat env upgrade <name>`

Move this environment's sandboxes onto its latest version. Live sandboxes get the new configuration pushed immediately, with any secret the new version withholds scrubbed off the machine first. Stopped sandboxes pick it up when they resume. Nothing upgrades on its own.

```bash theme={null}
boat env upgrade base
```

<Warning>
  A secret the new version withholds is deleted from the sandbox's disk, not hidden. Re-pinning to the older version does not bring it back.
</Warning>

### `boat host <id> <port>`

Expose a running service inside a sandbox on a stable HTTPS URL without opening an interactive SSH session.

```bash theme={null}
boat host bx_f7k2q9hd 3000
boat host bx_f7k2q9hd 3000 --title "Login preview"
boat host bx_f7k2q9hd 3000 --public
boat host bx_f7k2q9hd 3000 --json
```

The command opens the sandbox firewall, registers the HTTPS subdomain, and prints the URL. Calling it again for the same sandbox and port returns the same URL.

<Warning>
  The service you expose must listen on `0.0.0.0`, not only on `localhost` or `127.0.0.1`.
</Warning>

| Flag              | Default | Description                                                                   |
| ----------------- | ------- | ----------------------------------------------------------------------------- |
| `--title <title>` | none    | Set the display title for the hosted port.                                    |
| `--private`       | on      | Require the generated `_token` query parameter to access the URL.             |
| `--public`        | off     | Clear any saved access token and return a URL that does not require `_token`. |

### `boat scp`

Copy files to/from a sandbox. Use `bx_<id>:/path` as the remote address.

```bash theme={null}
# Download a file
boat scp bx_f7k2q9hd:/home/user/output.zip ./output.zip

# Upload a file
boat scp ./local-file.txt bx_f7k2q9hd:/home/user/

# Copy a directory recursively
boat scp --recursive ./my-project bx_f7k2q9hd:/home/user/
```

| Flag          | Description                  |
| ------------- | ---------------------------- |
| `--recursive` | Copy directories recursively |

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

### `boat forward <id>`

Forward one TCP port from a sandbox to your local machine.

```bash theme={null}
boat forward bx_f7k2q9hd --remote 8080
boat forward bx_f7k2q9hd --remote 3000 --bind 0.0.0.0
```

| Flag              | Default        | Description                |
| ----------------- | -------------- | -------------------------- |
| `--remote <port>` | required       | Remote port on the sandbox |
| `--local <port>`  | same as remote | Local port to bind         |
| `--bind <addr>`   | `127.0.0.1`    | Local bind address         |

#### `--reverse`: reach your machine from the sandbox

`--reverse` sends the tunnel the other way. A port on your machine becomes
reachable inside the sandbox at `127.0.0.1:<remote>`, so an agent in the sandbox can
call an MCP server, a local model, or a webhook receiver that never leaves your
laptop.

```bash theme={null}
# your app on localhost:7777 answers at 127.0.0.1:7777 inside the sandbox
boat forward bx_f7k2q9hd --reverse --local 7777

# a local model on 11434 shows up inside the sandbox on 11500
boat forward bx_f7k2q9hd --reverse --local 11434 --remote 11500
```

| Flag              | Default       | Description                                                                                                          |
| ----------------- | ------------- | -------------------------------------------------------------------------------------------------------------------- |
| `--local <port>`  | required      | Port on your machine to expose                                                                                       |
| `--remote <port>` | same as local | Port the sandbox listens on                                                                                          |
| `--bind <addr>`   | `127.0.0.1`   | Bind address **inside the sandbox**. Anything other than loopback needs `GatewayPorts` enabled on the sandbox's sshd |

Both directions run in the foreground until Ctrl+C. If the tunnel drops because
the sandbox moved (`boat stop` then `boat 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 `boat ssh`, and the sandbox side listens on loopback only. For a public
HTTPS URL for a port inside the sandbox, use [`boat host`](#sandbox-host-id-port).

### `boat desktop <id>`

Open the sandbox's desktop streaming URL in your browser.

```bash theme={null}
boat desktop bx_f7k2q9hd
boat desktop bx_f7k2q9hd --vnc
boat desktop bx_f7k2q9hd --vnc --public
```

By default the desktop streams over Moonlight (60fps WebRTC). On restrictive or
low-bandwidth networks where WebRTC is choppy or won't connect, use `--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.

| Flag       | Default | Description                                                         |
| ---------- | ------- | ------------------------------------------------------------------- |
| `--vnc`    | off     | Stream over VNC instead of Moonlight. More stable on poor networks. |
| `--public` | off     | With `--vnc`, return a URL that does not require an access token.   |

<Note>
  The first `--vnc` on a sandbox takes a few seconds while it prepares the VNC
  stack (the CLI shows a spinner); subsequent opens are instant.
</Note>

### `boat browser <id>`

Return a browser-only stream from the same running sandbox.

```bash theme={null}
boat browser bx_f7k2q9hd
boat browser bx_f7k2q9hd --profile /data/chrome-profile
```

| Flag               | Default                      | Description                                                                         |
| ------------------ | ---------------------------- | ----------------------------------------------------------------------------------- |
| `--profile <path>` | a managed dir on the sandbox | Absolute path on the sandbox used as the Chrome user data dir (the Chrome profile). |

`--profile` lets you keep several signed-in Chrome profiles on one sandbox 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 sandbox filesystem, snapshots and forks carry it
along. Switching profiles restarts Chrome and leaves the stream up. Sandboxes
created before this feature return `chrome_profile_unsupported`; recreate the
sandbox 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 sandbox, 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 sandboxes receive the browser-view files at provision. Treat the URL as a secret.

This is display and input confinement, not isolation from the sandbox itself. Code already running in the sandbox, especially code with root access, can inspect or modify the browser process and its data.

***

## Snapshots

### `boat snapshots [id]`

List snapshots across your sandboxes, or for one sandbox.

```bash theme={null}
boat snapshots
boat snapshots bx_f7k2q9hd
```

### `boat snapshot <id> <name>`

Save a sandbox's disk under a name, so `boat new --from <name>` can deploy it as many times as you like. Reuse a name to replace it.

```bash theme={null}
boat snapshot bx_f7k2q9hd web-stack
boat snapshot current web-stack     # from inside a sandbox
boat snapshot rm web-stack          # remove it and release its storage
```

Saving from a running sandbox takes a fresh capture first, so it can run for minutes; the CLI polls until the snapshot settles at `ready`. You can keep up to 10 named snapshots. See [Snapshots & Copies](/snapshots).

### `boat snapshot latest|tree|pull`

Inspect or download a snapshot. Works while the sandbox is stopped.

```bash theme={null}
boat snapshot latest bx_f7k2q9hd
boat snapshot tree <snapshotId>              # files and sizes
boat snapshot pull <snapshotId> -o ./restore # download and reassemble
```

`pull` writes `home_user/` (your `/home/user`) and `docker/` (named volumes). See [Snapshots](/snapshots).

### `boat 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 `boat snapshot rm <name>` instead.

```bash theme={null}
boat snapshot delete <snapshotId>
boat snapshot delete <snapshotId> --yes
```

See [Data retention and deletion](/data-retention).

***

## AI agents

### `boat prompt [id]`

Send a natural-language prompt to an AI agent running inside the sandbox. Omit the ID to target the current sandbox of your shell (the last `boat new` there), or the sandbox you are running inside. Omit `--provider` to use the default harness and model selected on the Agents page of the dashboard.

```bash theme={null}
boat prompt "Fix the failing tests in this repo"
boat prompt bx_f7k2q9hd --provider claude "Fix the failing tests in this repo"
boat prompt bx_f7k2q9hd --provider codex --model gpt-5.4-mini --reasoning-effort xhigh "Refactor auth module"
boat prompt --attach ./error.png --attach ./trace.log "Why does this crash?"
```

| Flag                 | Required | Values                                                                                                                                                                |
| -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--provider`         | no       | `claude`, `codex`, `pi`, `opencode`, `prime`, `kimi`. Default: the Agents page default harness                                                                        |
| `--model`            | no       | Provider-specific model name. Default: the model selected for that harness on the Agents page                                                                         |
| `--reasoning-effort` | no       | Provider/model-specific thinking level such as `none`, `low`, `medium`, `high`, `xhigh`, or `max`. Which levels a model accepts varies; some have none                |
| `--new`              | no       | Start a new conversation instead of continuing this shell's current one                                                                                               |
| `--resume <id>`      | no       | Continue a specific conversation by id (or `current` for this shell's)                                                                                                |
| `--attach <path>`    | no       | Attach a file (image, PDF, any file). Repeat for multiple. Alias `--image`. Saved on the sandbox under `~/attachments`; images are also sent to vision-capable models |

Run `boat prompt --help` to fetch the current provider, model, and reasoning-effort list from the Boat API.

#### Conversations (running agents in parallel)

A sandbox runs **many conversations at once**, each with its own history, so one sandbox can drive several independent agent tasks (different features, different users, a demo) in parallel rather than queuing them one behind another.

```bash theme={null}
boat prompt --new "Start on the billing refactor"     # conversation A
boat prompt --new "Investigate the flaky test"        # conversation B, runs alongside A
boat prompt --resume <A> "Now add tests"              # continue a specific conversation
boat prompt "quick follow-up"                          # continues THIS shell's current conversation
boat conversations                                     # list them all, with ids to resume
```

* **`--new`** starts a fresh conversation and makes it this shell's current one. Every prompt prints its conversation id right under `queued:` (`conversation: <id>`, with `(new)` when `--new` created it) and carries it as `conversationId` in `--json`.
* **`--resume <id>`** continues a specific conversation; `--resume current` targets this shell's current one.
* **No flag** continues this shell's current conversation. The "current conversation" is scoped to your shell, exactly like the `current` sandbox id, so two shells (or two people) prompting the same sandbox each keep their own thread and don't disturb each other.
* **[`boat conversations`](#sandbox-conversations-id)** lists every conversation on the sandbox 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-sandbox limit that scales with the sandbox's memory; beyond it, further prompts queue and start as turns finish. Continue a conversation on a different `--provider` and its history carries across the switch.

How harnesses, conversations, the agent lifecycle, and parallel runs fit together, with diagrams, is on [Integrated agents](/integrated-agents).

***

## Account

### `boat limits`

Show remaining machine starts, compute time, and credits. Create, fork, and resume each count as one start.

```bash theme={null}
boat limits
boat limits --json
```

Human output prints remaining starts per minute, hour and day, remaining compute in hours, and remaining credit-pack dollars. `--json` is the same payload the API returns, including `starts.minute.remaining`, `creditBalanceHours`, and `packBalanceDollars`.

### `boat usage <id>`

Show the machine time one sandbox has consumed and what it costs, over its whole life or a window. Works on running and stopped sandboxes; a running sandbox'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).

```bash theme={null}
boat usage bx_f7k2q9hd
boat usage bx_f7k2q9hd --since 2026-09-01 --until 2026-10-01
boat usage bx_f7k2q9hd --json
```

| Flag             | Default          | Description                                     |
| ---------------- | ---------------- | ----------------------------------------------- |
| `--since <time>` | sandbox creation | Count from this time, ISO 8601 or Unix seconds  |
| `--until <time>` | now              | Count up to this time, ISO 8601 or Unix seconds |

Human output prints billable machine time (the sandbox type's multiplier applied), the cost at list price, and the window. `--json` is the API payload: `seconds`, `dollars`, `secondsPerDollar`, `sandboxType`, `billingMultiplier`, `since`, `until` and `running`. See [Per-sandbox usage](/billing#per-sandbox-usage).

### `boat 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 sandbox 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.

```bash theme={null}
boat dashboard
```

### `boat org`

Show, switch, create, transfer, or delete the **organization wallet** that new sandboxes bill to. One shared plan and balance. `boat org switch` is sticky; `--org` (or `BOAT_ORG`) overrides it for one command.

```bash theme={null}
boat org                     # show the active wallet
boat org list                # organizations you belong to
boat org switch acme         # bill new sandboxes to acme
boat org switch personal     # back to your own account
boat --org acme new          # bill one create to acme without switching
boat --org personal new      # bill one create to your account while an org is sticky
boat org create "Acme"
boat --org acme org transfer alice
boat --org acme org delete --yes
```

`boat org transfer` and `boat org delete` target the invocation scope (`--org` / `BOAT_ORG` if set, otherwise the sticky wallet). Deleting an org with `--org` does not clear a different sticky wallet.

### `boat data-retention`

Show or enable zero data retention, which queues every archived sandbox for permanent deletion.

```bash theme={null}
boat data-retention status
boat data-retention enable
boat data-retention enable --yes
```

Enabling requires a browser sign-in session (`boat login` without a key), not an API key. See [Data retention and deletion](/data-retention).

### `boat self-update`

Check for and install the latest CLI release on your current channel.

```bash theme={null}
boat self-update
```

Local configuration lives at `~/.config/ascii/boat/config.json`. `boat status` prints its path along with the resolved API URL, account and plan.
