# CLI Reference

The `bh` CLI is intentionally small. This page covers every command and flag.

```text
bh create <name> [--provider <name>] [--size <name>] [--region <region>] [--image <image>] [--team <team>] [--no-sync]
bh list
bh destroy <name> [--force]
bh rename <old-name> <new-name>
bh move <name> <team>
bh connect <name>
bh run <name> <cmd...>
bh sync up <name>
bh sync down <name> --force
bh status <name>
bh ssh-config install|refresh|uninstall
bh image ls|create|rm [...]
bh team list|create|switch|status|members|invite|boxes [...]
bh size list|plans|create|rm [...]
bh login [--backend-url <url>] [--no-open]
bh logout
bh config
bh version
```

Box names use lowercase letters, numbers, and hyphens, start and end with a
letter or number, and are at most 63 characters.

## bh create

```bash
bh create <name> [options]
```

Provisions a VM through the backend, waits until the BoxHaven agent and SSH
certificate trust are ready, then syncs the current directory to
`/opt/boxhaven/project`.

| Option | Description |
| --- | --- |
| `--no-sync` | Skip the create command's initial project sync |
| `--provider <name>` | Cloud provider for create (defaults to config or backend default) |
| `--size <name>` | Built-in or team-owned size shortcut; defaults to `small` |
| `--region <region>` | Provider region for create, passed through to the provider verbatim |
| `--image <image>` | Team image name or provider image ID |
| `--team <team>` | Team that owns the new box (defaults to your active team) |
| `--ssh-user <user>` | SSH user for create |
| `--backend-url <url>` | Remote backend API URL for create |

## bh size

```bash
bh size list [--provider <name>] [--region <region>] [--team <team>]
bh size plans [--provider <name>] [--region <region>] [--team <team>]
bh size create <name> --provider <name> --plan <slug> [--team <team>]
bh size rm <name> [--team <team>]
```

`list` shows the three built-in sizes plus the selected team's shortcuts.
`plans` shows available provider plans, including hardware, GPUs, and the
effective price with hourly, daily, and monthly estimates. In hosted BoxHaven
this is the BoxHaven price; without a commercial policy it is the provider
price. Team owners and admins can create or remove shortcuts.

## bh run

```bash
bh run <name> [--sync] <cmd...>
```

Runs a command on the box. Interactive commands — `claude`, `codex`,
`gemini`, `opencode`, `copilot`, `pi`, and a bare `shell`, `bash`, `sh`,
`zsh`, or `fish` — start or attach to the box's managed tmux session. Shells
with arguments (`bash -lc '...'`) and all other commands run directly over
SSH.

`bh run` does not mirror the local folder, so work done by agents on the box
is never overwritten by a routine command. Pass `--sync` to mirror the local
project first (this overwrites box-side edits).

Without a terminal (for example, when launched by another agent with captured
output), managed agent commands start detached and return. With a terminal,
they attach; press **Ctrl-b**, then **d** to disconnect and use `bh connect` to
reattach. If the managed session already exists, another `bh run` attaches to
that session instead of submitting a new task. Inspect the session output to
confirm that the model is working; a successful launch is not task completion.

Starting `claude` or `codex` forwards your newest local sessions for the
current project, so `claude --continue` on the box resumes the conversation
your laptop was having:

```bash
bh run work claude --continue
```

## bh connect

```bash
bh connect <name>
```

Attaches to the box's managed tmux session over direct SSH, starting a shell
session if none exists. Disconnecting leaves the session running.

## bh ssh-config

```bash
bh ssh-config install
bh ssh-config refresh
bh ssh-config uninstall
```

`install` adds a managed include to `~/.ssh/config`, creates a persistent
device key under `~/.boxhaven/ssh`, and writes `bh-<name>` aliases for ready
boxes. These aliases work with ordinary OpenSSH clients:

```bash
ssh bh-work
scp ./notes.txt bh-work:/opt/boxhaven/project/
rsync -az ./fixtures/ bh-work:/opt/boxhaven/project/fixtures/
```

OpenSSH transparently asks `bh` for a fresh short-lived certificate before
using an alias. Box lifecycle commands keep the generated aliases current;
`refresh` fetches the current box list explicitly. `uninstall` removes the
managed include and generated config while retaining the device key.

## bh sync

```bash
bh sync up <name>
bh sync down <name> --force
```

`sync up` pushes the local project to `/opt/boxhaven/project`, mirroring
deletions. `sync down` pulls the box's project back into the local checkout;
it overwrites local files by design, so it requires `--force`. Sync excludes
common dependency/cache directories such as `node_modules/`, `.next/`, and
`.venv/` by default and reads additional rsync-style exclude patterns from
`.boxhavenignore` at the project root. Excluded paths are preserved on the
receiver even when sync mirrors deletions. Sync completion reports elapsed time,
network bytes, changed bytes, and file counts.

### .boxhavenignore

Both sync directions read `.boxhavenignore` from the **local** project root.
The file adds one rsync exclude pattern per line; blank lines and `#` comments
are ignored. `.gitignore` is not used as a sync filter, and `!` does not
re-include files. Built-in exclusions cannot be reversed by this file.

For example, to keep local credentials, private data, and generated output out
of the box when they are not needed by the task:

```text
.env
.env.local
/private-data/
/dist/
*.log
```

A leading `/` anchors a pattern to the project root, and a trailing `/`
matches directories. A name such as `.env` matches at any depth. Excluded
files already on the receiver remain there; adding a pattern does not remove
a previously uploaded copy. Sync down also mirrors deletions, so use the
matching local task directory when retrieving work.

The [BoxHaven agent skill](/agent-skill) bundles this sync guidance for Codex
and Claude alongside the box creation, agent, and retrieval workflows.

## bh list

```bash
bh list
```

Lists your boxes with name, status, team, provider, size, and preview URL.
Status is reported from the machine agent's last heartbeat: `creating` until
bootstrap completes, then `online` when the agent has been seen within the
last five minutes, otherwise `offline`. An interrupted create with an uncertain
provider outcome reports `recovery required`; destroy that box and create it
again instead of attempting to connect.

## bh status

```bash
bh status <name>
```

Prints the full backend record for one box: provider, public IP, size,
region, image, SSH user, preview URL, source and project paths, repo and
branch, last sync time, agent last-seen time, and bootstrap state.

## bh rename

```bash
bh rename <old-name> <new-name>
```

Renames the box record while keeping the underlying provider VM, preview
hostname, SSH principal, and agent identity unchanged.

## bh move

```bash
bh move <name> <team>
```

Moves one of your boxes to another of your teams. See [Teams](/teams).

## bh destroy

```bash
bh destroy <name> [--force]
```

Destroys the box and its provider VM. Owners and admins can destroy team
boxes; members can only destroy their own (see `bh team destroy` below for
removing a teammate's box). Without `--force`, the CLI prompts for
confirmation in interactive terminals and refuses to continue in noninteractive
sessions.

## bh image

```bash
bh image ls [--provider <name>]
bh image create <machine> [--name <name>]
bh image rm <name-or-id> [--provider <name>] [--force]
```

Images are private to the active team. Names are unique within that team, must
start with a letter or number after normalization, and get no added prefix.
Ambiguous references that match different images by name and ID return a conflict.
Pass an unambiguous name or ID to `bh create --image <name-or-id>`
when creating a box; without `--provider` image commands use the backend's
default provider. `bh image rm` prompts unless `--force` is passed. See
[Images](/images) for the full workflow.

## bh team

```bash
bh team list
bh team create <name>
bh team switch <team>
bh team status
bh team members [--team <slug-or-id>]
bh team invite <email> [--role member|admin|owner] [--team <slug-or-id>]
bh team boxes [--team <slug-or-id>]
bh team destroy <box> [--force] [--team <slug-or-id>]
```

`--team` is optional when you belong to exactly one team. `bh team destroy`
removes a teammate's box; it requires the owner or admin role and prompts
unless `--force` is passed. See [Teams](/teams) for the team workflow.

## bh login

```bash
bh login [--backend-url <url>] [--no-open]
bh login [--backend-url <url>] --token <token>
```

Without `--token`, boxhaven opens a browser approval flow and also prints the
URL. `--no-open` prints the browser login URL without trying to open it.
`--token` stores an existing backend session token without calling the login
API.

## bh logout

```bash
bh logout
```

Revokes the backend session and clears the stored token.

## bh config

```bash
bh config
```

Prints the effective configuration: `backend_url`, `token` (redacted),
`ssh_user`, `provider`, `remote_name`, `command`, and `setup`.

## bh version

```bash
bh version
```

Prints the CLI version and platform.

All normal commands except help and version periodically refresh the latest
public GitHub release in the background. The cached result is reused for 24
hours; a newer release adds one concise notice to standard error, while an
offline or failed check produces no warning and never delays the command.

## Configuration Files And Environment

BoxHaven reads global config from `~/.config/boxhaven/config.toml` and project
config from `.boxhaven.toml`:

```toml
[remote]
backend_url = "https://api.boxhaven.dev"
token = "browser-granted-session-token"
ssh_user = "boxhaven"
provider = "hetzner"
setup = [
  "docker compose up -d db"
]
```

Environment overrides:

- `BOXHAVEN_BACKEND_URL`
- `BOXHAVEN_TOKEN`
- `GH_TOKEN` or `GITHUB_TOKEN` for GitHub repository access inside remote boxes
