# BoxLite CLI
Command-line interface for BoxLite — use BoxLite without writing code, with a familiar Docker/Podman-like experience.
For CLI development (build, test, adding commands), see [CLI Development Guide](../docs/development/cli.md).
**Platforms:** macOS (Apple Silicon), Linux (x86_64, ARM64)
## Overview
The BoxLite CLI (`boxlite`) lets you create, run, and manage BoxLite boxes from the terminal. It targets quick testing, shell scripting and automation, debugging, and demos.
### Key Features
- **Run** — Create a box from an image and run a command (interactive, TTY, or detached); supports `-p` (publish ports) and `-v` (volumes)
- **Create** — Create a box without running; supports `-p` and `-v`
- **Lifecycle** — Start, stop, restart, remove boxes
- **Inspect** — Show detailed box info (JSON, YAML, or Go template)
- **Exec** — Run commands inside a running box
- **Images** — Pull and list OCI images
- **Copy** — Copy files between host and box (`boxlite cp`)
- **Output formats** — Table, JSON, or YAML for list/images
- **Shell completion** — Bash, Zsh, Fish
## Installation
### One-line install (Linux & macOS Apple Silicon)
```bash
curl -fsSL https://sh.boxlite.ai | sh
```
Installs to `$HOME/.local/bin/boxlite`. For version pinning, a custom
install dir, and release-artifact verification (sigstore, `SHA256SUMS`,
`gh attestation verify`), see the [CLI Reference's Installation & Verification section](../../docs/reference/cli/README.md#installation--verification).
### cargo install (from source)
```bash
cargo install boxlite-cli
```
### cargo binstall (prebuilt binary)
```bash
cargo binstall boxlite-cli
```
### Homebrew
Coming soon
### Build from Source
```bash
# From repository root
git clone https://github.com/boxlite-ai/boxlite.git
cd boxlite
# Initialize submodules (required)
git submodule update --init --recursive
# Build the CLI
cargo build --release -p boxlite-cli
# Binary: target/release/boxlite
```
### System Requirements
| Platform | Architecture | Status |
|----------------|-----------------------|------------------|
| macOS | Apple Silicon (ARM64) | ✅ Supported |
| Linux | x86_64 | ✅ Supported |
| Linux | ARM64 | ✅ Supported |
| Windows (WSL2) | x86_64 | ✅ Supported |
| macOS | Intel (x86_64) | ❌ Not supported |
## Quick Start
### Run a one-off command
```bash
boxlite run python:slim python -c "print('Hello from BoxLite!')"
```
### Run interactively with a TTY
```bash
boxlite run -it alpine:latest /bin/sh
```
### Create a box and run in the background
```bash
# Create and start (prints box ID)
boxlite run -d --name mybox alpine:latest sleep 3600
# Run a command in the box
boxlite exec mybox echo "Hello"
# List boxes
boxlite list -a
# Stop and remove
boxlite stop mybox
boxlite rm mybox
```
### Pull an image and list images
```bash
boxlite pull alpine:latest
boxlite images
```
## Connecting to a remote server
To target a remote BoxLite REST server instead of the local runtime, sign in
with `boxlite auth login`. Three login methods are supported:
| Method | When to use | Token type |
|-------------|--------------------------------------------------------|----------------------|
| `api-key` | CI / automation, or a server-minted long-lived key | Opaque `blk_…` key |
| `browser` | Local developer machine with a desktop browser | OIDC access token |
| `device` | SSH session / headless container with no browser | OIDC access token |
Credential precedence is **`BOXLITE_API_KEY` env > stored file > unauthenticated** (local runtime).
The `--url` flag overrides the URL specifically without affecting credentials.
Multiple profiles coexist in one file via `--profile <name>` (or `BOXLITE_PROFILE` env var).
```bash
# Browser OIDC against a control plane (default for `auth login` on a TTY).
# Requires the IdP admin to have registered `http://127.0.0.1:5555/callback`
# in the SPA application's Allowed Callback URLs — see apps/infra/README.md
# "OIDC provider setup" for the one-time setup.
boxlite --profile cloud auth login --url https://<your-control-plane>/api
# Same target, headless (SSH / no browser): prints a code + URL to type into
# any browser on another device.
boxlite --profile cloud auth login --url https://<your-control-plane>/api --no-browser
# Local boxlite serve with a static API key (unchanged from the original behavior).
boxlite --profile local auth login --url http://localhost:8100 # interactive paste
echo "$KEY" | boxlite --profile local auth login --url http://localhost:8100 --api-key-stdin
# CI via env vars only — no `auth login` call needed.
BOXLITE_API_KEY=$KEY BOXLITE_REST_URL=https://<your-server> boxlite list
```
Credentials are stored at `~/.boxlite/credentials.toml` (perms `0600`). OIDC
sessions auto-refresh on use when within 5 minutes of expiry; if the refresh
token is rejected (`invalid_grant`) the CLI prompts you to re-run `auth login`.
## Commands Reference
> For an exhaustive man-page-style reference (shared flag groups, volume/port grammar,
> exit codes, configuration file format), see the
> [CLI Reference](../../docs/reference/cli/README.md).
### Global flags
Available for all commands:
| Flag | Description |
|------|-------------|
| `--debug` | Enable debug output. Precedence: `--debug` > `RUST_LOG` env > default (`warn`). |
| `--home PATH` | BoxLite home directory (default: `~/.boxlite`). Overridden by `BOXLITE_HOME` |
| `--registry REGISTRY` | Image registry (repeatable; prepended to config) |
| `--config PATH` | JSON config file path (e.g. for `image_registries`) |
| `--url URL` | Connect to a remote BoxLite REST server instead of the local runtime. Env: `BOXLITE_REST_URL`. |
| `--profile NAME` | Named credential profile in `~/.boxlite/credentials.toml`. Lets one machine hold separate logins (e.g. `local` for `boxlite serve`, `cloud` for a remote control plane). Default `default`. Env: `BOXLITE_PROFILE`. |
| `--path-prefix VALUE` | Routing-slot value for the URL path (`/v1/<prefix>/boxes/...`). Opaque — the server decides what it means (organization, workspace, catalog, …). Captured automatically at `auth login` time from `Principal.path_prefix`. This flag overrides the stored profile's value for credentials with scope over multiple routing values. Unset / empty → URL skips the segment (`/v1/boxes/...`), the canonical shape for single-tenant deployments like `boxlite serve`. Env: `BOXLITE_REST_PATH_PREFIX`. |
### `boxlite auth login`
Log in to a BoxLite REST server. Supports three flows that all save to
`~/.boxlite/credentials.toml` (perms `0600`):
- **API key** — paste or stdin. Long-lived, org-scoped. Good for CI, SDK
integrations, `boxlite serve` setups.
- **Browser OIDC** — Authorization Code + PKCE against the IdP that
`apps/api` is configured for (Auth0, Dex, Okta, etc.). Opens the system
browser; the CLI listens on `127.0.0.1:5555` for the callback. Mints an
access token + refresh token; the latter is used to silently refresh
within 5 minutes of expiry.
- **Device code OIDC** — RFC 8628. Headless / SSH-friendly: prints a short
code + URL to enter on any browser on another device. Same token type
and refresh behavior as the browser flow.
When `--method` is unset, the CLI infers it: piped stdin → `api-key` (CI-safe),
TTY → interactive picker, `$SSH_CONNECTION` set → silent fallback to `device`.
**Usage:** `boxlite auth login [OPTIONS]`
| Option | Description |
|--------|-------------|
| `--url URL` | Server URL (default: `http://localhost:8100`, matching `boxlite serve`). For cloud control planes include the `/api` prefix, e.g. `https://api.example.com/api`. |
| `--method <api-key\|browser\|device>` | Explicit flow choice. Overrides inference. |
| `--api-key-stdin` | Read the API key from stdin (one line). The flag takes no value, so the secret never appears on argv. Implies `--method api-key`. |
| `--no-browser` | Skip the browser; use device code instead. Implies `--method device`. |
| `--callback-port <PORT>` | Local port for the browser-flow callback (default `5555`). **Must match an entry in the IdP's allow-list byte-for-byte** — a different port produces "Callback URL mismatch" exactly like no entry at all. |
| `--issuer URL` | OIDC issuer URL. Overrides what `GET /api/config` returns. Useful for self-hosted Dex tenants where the discovery is wrong. |
| `--client-id ID` | OIDC client_id. Overrides `/api/config`. |
| `--audience VAL` | OIDC audience. Auth0 requires it; Dex tolerates `None`. |
Global flags also apply — most importantly `--profile NAME` to log in to a
specific credential profile (default `default`).
**Examples:**
```bash
# Cloud control plane via browser (most common; opens system browser).
boxlite --profile cloud auth login --url https://<your-control-plane>/api
# Same target, headless: prints a code + URL.
boxlite --profile cloud auth login --url https://<your-control-plane>/api --no-browser
# Local boxlite serve with paste-API-key (interactive).
boxlite --profile local auth login --url http://localhost:8100
# CI: API key from stdin (nothing on argv).
echo "$KEY" | boxlite --profile local auth login --url http://localhost:8100 --api-key-stdin
```
**Deployment-side setup (one-time, by the IdP admin):**
Browser and device flows fail with "Callback URL mismatch" until the IdP
knows about the CLI's loopback URL. For Auth0 see
`apps/infra/README.md` "OIDC provider setup" — add
`http://127.0.0.1:5555/callback` to the SPA Application's
**Allowed Callback URLs**. For Dex see `apps/dex/config.yaml` — the same
URL goes under the `boxlite` static client's `redirectURIs`, plus
`oauth2.deviceFlow: {}` at the top level for device flow.
### `boxlite auth logout`
Remove stored credentials at `~/.boxlite/credentials.toml`. Prompts for confirmation unless `--yes` is given.
**Usage:** `boxlite auth logout [OPTIONS]`
| Option | Short | Description |
|--------|-------|-------------|
| `--yes` | `-y` | Skip the confirmation prompt |
### `boxlite auth status`
Print the current authentication state: the logged-in URL, the source
(stored file vs env var), the credential type (API key vs OIDC), and for
OIDC sessions the access token's expiry. Offline — no network calls,
no secret material printed.
**Usage:** `boxlite auth status [--profile NAME]`
**Example output (API key):**
```
Logged in to: http://localhost:8100
Credential: API key (from ~/.boxlite/credentials.toml [local])
```
**Example output (OIDC session):**
```
Logged in to: https://api.boxlite.ai/api
Credential: OIDC bearer token (from ~/.boxlite/credentials.toml [cloud])
Expires: 2026-05-21T15:42:00+00:00
```
### `boxlite auth whoami`
Confirm the active credential's identity by making one authenticated
request to `GET /v1/me`. Unlike `auth status` (offline, only reports where
the credential came from), `whoami` shows the server-resolved principal,
organization, and scopes. Triggers a silent OIDC refresh if the access
token is within 5 minutes of expiry.
**Usage:** `boxlite auth whoami [--profile NAME]`
**Example output:**
```
Logged in as: dev@acme.test
Name: Dev McAcme
Principal: auth0|abc123 (user)
Organization: acme
Server: https://api.boxlite.ai/api
Scopes: box:read, box:write, box:exec, image:read, snapshot:read
```
### `boxlite run`
Create a box from an image and run a command.
**Usage:** `boxlite run [OPTIONS] IMAGE [COMMAND]...`
| Option | Short | Description |
|--------|-------|-------------|
| `--interactive` | `-i` | Keep STDIN open |
| `--tty` | `-t` | Allocate a pseudo-TTY |
| `--env KEY=VALUE` | `-e` | Set environment variables (repeatable) |
| `--workdir PATH` | `-w` | Working directory in the box |
| `--publish PORT` | `-p` | Publish box port to host (e.g. `8080:80`, `8080:80/tcp`) |
| `--volume VOLUME` | `-v` | Mount a volume (e.g. `hostPath:boxPath`, `boxPath` for anonymous) |
| `--cpus N` | | CPU limit |
| `--memory MiB` | | Memory limit (MiB) |
| `--name NAME` | | Name the box |
| `--detach` | `-d` | Run in background, print box ID |
| `--rm` | | Remove the box when it exits |
**Examples:**
```bash
boxlite run alpine:latest echo "Hello"
boxlite run -it --rm alpine:latest /bin/sh
boxlite run -d --name openclaw -p 18789:18789 ghcr.io/openclaw/openclaw:main
boxlite run -v /host/data:/app/data alpine:latest cat /app/data/hello.txt
```
### `boxlite create`
Create a new box without running a command.
**Usage:** `boxlite create [OPTIONS] IMAGE`
| Option | Short | Description |
|--------|-------|-------------|
| `--name NAME` | | Name the box |
| `--env KEY=VALUE` | `-e` | Environment variables |
| `--workdir PATH` | `-w` | Working directory |
| `--publish PORT` | `-p` | Publish box port to host (e.g. `8080:80`) |
| `--volume VOLUME` | `-v` | Mount a volume (e.g. `hostPath:boxPath`, or box path for anonymous) |
| `--cpus N` | | CPU limit |
| `--memory MiB` | | Memory limit (MiB) |
| `--detach` | `-d` | (create always “detaches”) |
| `--rm` | | Auto-remove when stopped |
**Examples:**
```bash
boxlite create --name mybox alpine:latest
boxlite create -p 18789:18789 -v /data:/app/data --name openclaw ghcr.io/openclaw/openclaw:main
boxlite start mybox
boxlite start openclaw
```
### `boxlite exec`
Run a command in a running box.
**Usage:** `boxlite exec [OPTIONS] BOX COMMAND [ARGS]...`
| Option | Short | Description |
|--------|-------|-------------|
| `--interactive` | `-i` | Keep STDIN open |
| `--tty` | `-t` | Allocate a TTY |
| `--env KEY=VALUE` | `-e` | Environment variables |
| `--workdir PATH` | `-w` | Working directory |
| `--detach` | `-d` | Run in background (don’t wait) |
**Example:**
```bash
boxlite exec -it mybox /bin/sh
```
### `boxlite list` (alias: `ls`, `ps`)
List boxes.
**Usage:** `boxlite list [OPTIONS]`
| Option | Short | Description |
|--------|-------|-------------|
| `--all` | `-a` | Show all boxes (default: running only) |
| `--quiet` | `-q` | Show only IDs |
| `--format FMT` | | Output format: `table`, `json`, `yaml` (default: `table`) |
### `boxlite start`
Start one or more stopped boxes.
**Usage:** `boxlite start BOX [BOX ...]`
### `boxlite stop`
Stop one or more running boxes.
**Usage:** `boxlite stop BOX [BOX ...]`
### `boxlite restart`
Restart one or more boxes.
**Usage:** `boxlite restart BOX [BOX ...]`
### `boxlite rm`
Remove one or more boxes.
**Usage:** `boxlite rm [OPTIONS] BOX [BOX ...]` or `boxlite rm [OPTIONS] --all`
| Option | Short | Description |
|--------|-------|-------------|
| `--force` | `-f` | Force remove (e.g. running box) |
| `--all` | `-a` | Remove all boxes (prompts unless `--force`) |
### `boxlite pull`
Pull an image from a registry.
**Usage:** `boxlite pull [OPTIONS] IMAGE`
| Option | Short | Description |
|--------|-------|-------------|
| `--quiet` | `-q` | Only print digest |
### `boxlite inspect`
Display detailed information on one or more boxes (JSON, YAML, or Go-style template).
**Usage:** `boxlite inspect [OPTIONS] [BOX ...]` or `boxlite inspect --latest`
| Option | Short | Description |
|--------|-------|-------------|
| `--latest` | `-l` | Inspect the most recently created box (cannot be used with BOX) |
| `--format FMT` | `-f` | Output: `json`, `yaml`, or a Go template (e.g. `{{.State.Status}}`, `{{.Id}}`). Default: `json`. Table format is not supported. |
**Examples:**
```bash
boxlite inspect mybox
boxlite inspect -f '{{.State.Status}}' mybox
boxlite inspect --latest -f yaml
boxlite inspect box1 box2 -f json
```
### `boxlite images`
List cached images.
**Usage:** `boxlite images [OPTIONS]`
| Option | Short | Description |
|--------|-------|-------------|
| `--all` | `-a` | Show all images (including intermediate) |
| `--quiet` | `-q` | Show only image IDs |
| `--format FMT` | | Output format: `table`, `json`, `yaml` |
### `boxlite cp`
Copy files or directories between host and box.
**Usage:** `boxlite cp [OPTIONS] SRC DST`
- **SRC / DST:** host path or `BOX:PATH` (e.g. `mybox:/app/data`).
| Option | Description |
|--------|-------------|
| `--follow-symlinks` | Follow symlinks when copying |
| `--no-overwrite` | Do not overwrite existing files |
| `--include-parent` | Include parent directory when copying from box (default: true) |
**Examples:**
```bash
boxlite cp ./local.txt mybox:/tmp/
boxlite cp mybox:/app/out ./output
```
### `boxlite info`
Display system-wide runtime information (version, paths, host/virtualization, box and image counts). Default output is YAML.
**Usage:** `boxlite info [OPTIONS]`
| Option | Description |
|--------|-------------|
| `--format FMT` | Output format: `yaml`, `json` (default: `yaml`). Table format is not supported. |
**Output fields:** `version`, `homeDir`, `virtualization`, `os`, `arch`, `boxesTotal`, `boxesRunning`, `boxesStopped`, `boxesConfigured`, `imagesCount`.
**Examples:**
```bash
boxlite info
boxlite info --format json
```
## Shell completion
Generate completion scripts for your shell:
```bash
# Bash
boxlite completion bash > /etc/bash_completion.d/boxlite
# or for current user
boxlite completion bash > ~/.local/share/bash-completion/completions/boxlite
# Zsh
boxlite completion zsh > "${fpath[1]}/_boxlite"
# Fish
boxlite completion fish > ~/.config/fish/completions/boxlite.fish
```
Then reload your shell or source the file.
## Environment variables
| Variable | Description |
|----------|-------------|
| `BOXLITE_HOME` | Runtime home directory (default: `~/.boxlite`). Overridden by `--home`. |
| `BOXLITE_API_KEY` | Long-lived API key sent as `Authorization: Bearer`. Overrides any stored credentials. |
| `RUST_LOG` | Log level: `trace`, `debug`, `info`, `warn`, `error`. Use `RUST_LOG=debug` for troubleshooting. |
## Configuration file
Use `--config PATH` to load a JSON config file. Useful for default registries and other options. See [Image registry configuration](../../docs/guides/image-registry-configuration.md) for details.
## Troubleshooting
### Image pull fails
- Check network and registry access.
- For private registries, see [Image registry configuration](../../docs/guides/image-registry-configuration.md) for details.
- **"Failed to pull manifest"** or **"error sending request for url"** (e.g. to `index.docker.io`): often network-related or Docker Hub rate limit/access in some regions. Retry later, use a mirror, or configure registries via `--registry` / `--config`. See [issue #190](https://github.com/boxlite-ai/boxlite/issues/190) for discussion.
- Enable debug output: `boxlite --debug pull IMAGE` or `RUST_LOG=debug boxlite pull IMAGE`.
### Box fails to start
- Enable debug output: `boxlite --debug run IMAGE [COMMAND]...` or `RUST_LOG=debug boxlite run IMAGE [COMMAND]...`.
## Further documentation
- [BoxLite README](../../README.md) — Project overview and SDK quick starts
- [Getting started](../../docs/getting-started/README.md) — Prerequisites and platform setup
- [Reference](../../docs/reference/README.md) — Python, Node, Rust, C API reference
## License
Licensed under the Apache License, Version 2.0. See [LICENSE](../LICENSE) for details.