boxlite-cli 0.11.0

Command-line interface for BoxLite container runtime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
# 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/contributing/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 or prepared rootfs and run a command (interactive, TTY, or detached); supports `-p` (publish ports) and `-v` (volumes)
- **Create** — Create a box from an image or prepared rootfs 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 in a running or safely resumable 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

The `boxlite agent` namespace groups nine commands for agents and turns,
including steering. BOX is positional; creation uses `--agent IMPLEMENTATION`.
Other commands use `--agent ID` and `--turn TURN` to select existing work.
See the [agent CLI reference](../../docs/reference/cli/README.md#boxlite-agent)
for commands, grouped options and error behavior.

## 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    |

Create a named context with `auth login`, then select it once with
`boxlite context use NAME`. Subsequent commands use that context automatically.
`BOXLITE_API_KEY` overrides the selected context's bearer credential; its URL and
tenant path remain in effect.

```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
# "Callback URL mismatch" for the one-time setup.
boxlite --context cloud auth login --url https://<your-control-plane>/api
boxlite context use cloud
boxlite list

# Same target, headless (SSH / no browser): prints a code + URL to type into
# any browser on another device.
boxlite --context 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 --context local auth login --url http://localhost:8100  # interactive paste
echo "$KEY" | boxlite --context 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_HOME>/credentials.toml` (default
`~/.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`.

`boxlite context ls` marks the effective context with `*`; `boxlite context show`
prints its name. `--context NAME` or `BOXLITE_CONTEXT=NAME` overrides the saved
selection without changing it. The old `--profile` and `BOXLITE_PROFILE` spellings
remain supported. Login saves credentials but does not change the current context.

`boxlite context use default` restores the pre-context configuration: a URL
override or existing `default` credentials select REST; otherwise commands use
the embedded runtime. Context selection is stored separately in
`<BOXLITE_HOME>/cli.toml`. See [context selection and precedence](../../docs/reference/cli/README.md#boxlite-context)
for URL overrides and missing contexts.

## 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

Place these options before the top-level command or after the complete command
path. A parent command accepts only options shared by all of its children, so
write `boxlite auth status --url URL` (or `boxlite --url URL auth status`), not
`boxlite auth --url URL status`. Each command's help shows only options it can
use. Executing invocations reject unsupported options; Clap's `--help` and
`--version` display actions remain terminal.

| Flag | Scope | Description |
|------|-------|-------------|
| `--debug` | all commands except `completion` | Enable debug output. Precedence: `--debug` > `RUST_LOG` env > default (`warn`). |
| `--home PATH` | local runtime + credentials + contexts | Absolute BoxLite home directory (default: `~/.boxlite`). `BOXLITE_HOME` is the env spelling. |
| `--registry REGISTRY` | `run`, `create`, `pull`, `serve` | Image registry (repeatable; prepended to config). |
| `--config PATH` | local-capable commands | JSON config file path (e.g. for `home_dir` and `image_registries`). |
| `--url URL` | REST-capable commands | Connect to a remote BoxLite REST server instead of the local runtime. Env: `BOXLITE_REST_URL`. |
| `--context NAME` | REST-capable commands + `auth` + `context` | Override the saved current context. Env: `BOXLITE_CONTEXT`. Legacy aliases: `--profile`, `BOXLITE_PROFILE`. |
| `--path-prefix VALUE` | REST box/volume commands | Routing-slot value for `/v1/<prefix>/...`; overrides the selected context. Env: `BOXLITE_REST_PATH_PREFIX`. |

### Network policy updates

```bash
boxlite network inbound mybox enabled
boxlite network outbound mybox enabled --allow-net '*.example.com'
boxlite network outbound mybox enabled  # default empty list: unrestricted
boxlite network outbound mybox disabled
boxlite inspect mybox
```

The box and mode are required. Repeat `--allow-net` for multiple outbound rules.
Omit `--allow-net` for the default empty list; `''` is rejected, as at create.
Inbound accepts no list; disabled outbound accepts no `--allow-net`. Each command replaces
one direction without retaining earlier rules. See [networking](../../docs/guides/networking.md#updating-a-running-box)
for runtime support, connection lifetime and persistence.

### Guest SSH

```bash
boxlite run -d --name dev alpine:latest tail -f /dev/null
boxlite ssh configure dev --host-key ./host_key --login alice \
  --authorized-key ~/.ssh/id_ed25519.pub
boxlite ssh forward dev
# stdout: tcp://127.0.0.1:52811   stderr: host key, fingerprint, connect hint
ssh -p 52811 -l alice 127.0.0.1
```

`boxlite ssh configure` replaces the configuration and prints `SshStatus` JSON.
Then `forward` opens a local listener until Ctrl-C, leaving the configuration and
generation unchanged. `status` and `disable` also print `SshStatus` JSON.
The CLI never generates keys.
See [`boxlite ssh`](../../docs/reference/cli/README.md#boxlite-ssh) for flags and the
[SSH guide](../../docs/guides/ssh.md#cli) for lifecycle rules.

### `boxlite auth login`

Log in to a BoxLite REST server. Supports three flows that all save to
`<BOXLITE_HOME>/credentials.toml` (default `~/.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.

On a BoxLite Auth0 deployment, an existing unverified database account must
complete the hosted verification Form through browser login:

```bash
boxlite --context cloud auth login --url https://<your-control-plane>/api --method browser
```

Auth0 Forms cannot run inside refresh-token, device-code, or other
non-interactive exchanges. An SSH user should finish verification through the
BoxLite dashboard in a separate browser, then retry the device login.

When `--method` is unset, the CLI infers it: piped stdin → `api-key` (CI-safe),
while a TTY opens the interactive picker. If Browser is selected from that
picker in a detected headless/SSH environment, the CLI falls back to `device`.

**Usage:** `boxlite auth login [OPTIONS]`

| Option | Description |
|--------|-------------|
| `--url URL` | Server URL (precedence: flag, `BOXLITE_REST_URL`, selected context URL, then `https://api.boxlite.ai`). Self-hosted deployments may require a base path such as `/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. Selects API-key login and conflicts with explicit browser/device methods. |
| `--no-browser` | Use device code when no method is explicit; conflicts with API-key stdin and explicit API-key/browser methods. |
| `--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`. |

`--context NAME` selects the context to create or update; when omitted, login
uses the effective current context. Login accepts both `--context` and `--url`
because the URL configures that context. `--home PATH` selects the credential and
context preference directory. Login does not switch the saved current context.

**Examples:**

```bash
# Cloud control plane via browser (most common; opens system browser).
boxlite --context cloud auth login --url https://<your-control-plane>/api

# Same target, headless: prints a code + URL.
boxlite --context cloud auth login --url https://<your-control-plane>/api --no-browser

# Local boxlite serve with paste-API-key (interactive).
boxlite --context local auth login --url http://localhost:8100

# CI: API key from stdin (nothing on argv).
echo "$KEY" | boxlite --context local auth login --url http://localhost:8100 --api-key-stdin
```

**Deployment-side setup (one-time, by the IdP admin):**

Browser PKCE login needs the loopback callback `http://127.0.0.1:5555/callback`
registered in the client's **Allowed Callback URLs**. Device login does not use
that callback. It uses the provider's discovered `device_authorization_endpoint`;
older Dex deployments without this field retain the `/device/code` fallback.

For Auth0, configure a public **Native** CLI application with the **Device Code**
grant enabled. Enable **Refresh Token** for the client and **Allow Offline Access**
for the target API to issue renewable sessions. Pass its public ID with
`--client-id` if `/api/config` advertises the dashboard SPA application. Flags
win over server values; `OIDC_CLIENT_ID` is a fallback, not an override for
`/api/config`. Do not provide a client secret. An `unauthorized_client` error
can mean an unknown/wrong client or a disabled grant: check the CLI client ID
and issuer, then ask the IdP administrator to verify the Device Code grant.
For Dex, enable `oauth2.deviceFlow: {}` in `apps/dex/config.yaml`.

Device login prints the verification URL and one-time user code on stderr.
Complete authorization in a browser while the CLI polls. Ctrl-C cancels polling;
denied, expired, and cancelled attempts do not save a partial session. Retry
`auth login` to obtain a new code. Server response descriptions and bodies are
excluded from device-login errors to avoid exposing credentials.

### `boxlite auth logout`

Remove the selected context from `<BOXLITE_HOME>/credentials.toml` (default
`~/.boxlite/credentials.toml`). Prompts for confirmation unless `--yes` is given.
The current context stays selected; commands fail if its credentials are missing.
Log in again or run `boxlite context use default` to switch back explicitly.

**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. With only `BOXLITE_API_KEY` set, it reports
`https://api.boxlite.ai`.

**Usage:** `boxlite auth status [--url URL | --context NAME]`

**Example output (API key):**

```
Context:         local (from saved context)
Logged in to:    http://localhost:8100
Credential:      API key (from <BOXLITE_HOME>/credentials.toml [local])
```

**Example output (OIDC session):**

```
Context:         cloud (from saved context)
Logged in to:    https://api.boxlite.ai
Credential:      OIDC bearer token (from <BOXLITE_HOME>/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,
path prefix, and scopes. Triggers a silent OIDC refresh if the access
token is within 5 minutes of expiry.

**Usage:** `boxlite auth whoami [--url URL | --context NAME]`

**Example output:**

```
Logged in as:    dev@acme.test
Name:            Dev McAcme
Principal:       auth0|abc123 (user)
Path prefix:     acme
Server:          https://api.boxlite.ai
Scopes:          box:read, box:write, box:exec, image:read, snapshot:read
```

### `boxlite run`

Create a box from an image (or a prepared rootfs via `--rootfs`) and run a
command, with docker's semantics: `COMMAND` replaces the image's `CMD`, the
image's `ENTRYPOINT` is prepended, and the result **is** the container's init
(PID 1). Omit it and the image's own default runs.

The box lives exactly as long as that command. When it exits, the box stops and
takes its exit code — `boxlite inspect -f '{{.State.ExitCode}}' NAME` reads it
back. `exec` against a box whose command has finished is refused rather than
silently restarting it, because restarting would run the command a second time.

**Usage:**

- `boxlite run [OPTIONS] IMAGE [COMMAND]...`
- `boxlite run [OPTIONS] --rootfs PATH [COMMAND]...`

| Option | Short | Description |
|--------|-------|-------------|
| `--rootfs PATH` | | Use a prepared rootfs path instead of pulling/resolving an image |
| `--interactive` | `-i` | Keep STDIN open; conflicts with `--detach` |
| `--tty` | `-t` | Allocate a pseudo-TTY; requires TTY-attached stdin, even with `--detach` |
| `--env KEY[=VALUE]` | `-e` | Set environment variables; a bare key inherits its host value (repeatable) |
| `--workdir PATH` | `-w` | Working directory in the box |
| `--user NAME[:GROUP]` | `-u` | Run as a name/uid and optional group/gid |
| `--entrypoint EXEC` | | Override the image entrypoint |
| `--publish PORT` | `-p` | Publish a TCP box port locally (`80` = automatic host port, `8080:80` = fixed) |
| `--volume VOLUME` | `-v` | Mount a volume: `name:/box` for a managed volume, `./path:/box` for a host bind, `/box` for anonymous |
| `--cpus N` | | CPU limit |
| `--memory MiB` | | Memory limit (MiB) |
| `--disk-size GB` | | Sparse rootfs disk size; smaller values than the base image are ignored |
| `--cap-add CAPABILITY` | | Add a Linux capability (repeatable; accepts `CAP_` prefix or `ALL`) |
| `--cap-drop CAPABILITY` | | Drop a Linux capability (repeatable; accepts `CAP_` prefix or `ALL`) |
| `--network <enabled\|disabled>` | | Outbound network mode (default `enabled`) |
| `--allow-net HOST` | | Restrict egress to an exact host, wildcard domain, IPv4, or IPv4 CIDR; repeatable and implies enabled networking |
| `--inbound <enabled\|disabled>` | | Inbound network mode (default `disabled`) |
| `--net-tx-kbps KBPS` | | Cap what the box sends, in kilobits/sec; `0` or unset is uncapped. Local runtime only; verified on Linux |
| `--net-rx-kbps KBPS` | | Cap what reaches the box, in kilobits/sec; `0` or unset is uncapped. Local runtime only; verified on Linux |
| `--name NAME` | | Name the box |
| `--detach` | `-d` | Run in background, print box ID |
| `--rm` | | Remove the box when it stops; conflicts with lifecycle deadlines and is ignored with `--detach` |
| `--auto-stop DURATION` | | Stop the box after this much inactivity; `0` disables |
| `--auto-delete DURATION` | | Delete the box this long after it stops; `0` disables |
| `--no-auto-resume` | | Ask a REST server to refuse implicit resume after a box has run; first boot is unaffected, and the embedded runtime records but does not enforce it |
| `--security <enable\|disable>` | | Embedded-runtime sandbox security (default `enable`; env `BOXLITE_SECURITY`); REST servers own this policy |

`-p` is explicit local publication. Remote REST contexts reject it and direct
the caller to `boxlite network tunnel`.

`DURATION` is seconds when bare, or suffixed: `30s`, `15m`, `2h`, `7d`. The two
deadlines are swept by a server — `boxlite serve` or the cloud — so against the
embedded runtime they are refused rather than silently reinterpreted. See the
[CLI reference](../../docs/reference/cli/README.md) for the sweep's granularity
and how `--rm` differs between the two.

**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 run --rootfs /path/to/rootfs /bin/sh
boxlite run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx:alpine
```

### `boxlite create`

Create a box without starting it.

`COMMAND` is stored, not run — it becomes the box's main command when the box is
next started, exactly as under `run`. A box created *with* one is a job, and
`exec`/`cp` will not start it implicitly (that would run the command); start it
deliberately with `boxlite start`. A box created *without* one boots the image's
default, and `exec` still starts it on demand.

**Usage:**

- `boxlite create [OPTIONS] IMAGE [COMMAND]...`
- `boxlite create [OPTIONS] --rootfs PATH [COMMAND]...`

| Option | Short | Description |
|--------|-------|-------------|
| `--rootfs PATH` | | Use a prepared rootfs path instead of pulling/resolving an image |
| `--name NAME` | | Name the box |
| `--env KEY[=VALUE]` | `-e` | Environment variables; a bare key inherits its host value |
| `--workdir PATH` | `-w` | Working directory |
| `--entrypoint EXEC` | | Override the image entrypoint |
| `--publish PORT` | `-p` | Publish a TCP box port locally (`80` = automatic host port, `8080:80` = fixed) |
| `--volume VOLUME` | `-v` | Mount a volume: `name:/box` for a managed volume, `./path:/box` for a host bind, `/box` for anonymous |
| `--cpus N` | | CPU limit |
| `--memory MiB` | | Memory limit (MiB) |
| `--disk-size GB` | | Sparse rootfs disk size; smaller values than the base image are ignored |
| `--cap-add CAPABILITY` | | Add a Linux capability (repeatable; accepts `CAP_` prefix or `ALL`) |
| `--cap-drop CAPABILITY` | | Drop a Linux capability (repeatable; accepts `CAP_` prefix or `ALL`) |
| `--network <enabled\|disabled>` | | Outbound network mode (default `enabled`) |
| `--allow-net HOST` | | Restrict egress to an exact host, wildcard domain, IPv4, or IPv4 CIDR; repeatable and implies enabled networking |
| `--inbound <enabled\|disabled>` | | Inbound network mode (default `disabled`) |
| `--net-tx-kbps KBPS` | | Cap what the box sends, in kilobits/sec; `0` or unset is uncapped. Local runtime only; verified on Linux |
| `--net-rx-kbps KBPS` | | Cap what reaches the box, in kilobits/sec; `0` or unset is uncapped. Local runtime only; verified on Linux |
| `--auto-stop DURATION` | | Stop the box after this much inactivity; `0` disables; requires a REST server |
| `--auto-delete DURATION` | | Delete the box this long after it stops; `0` disables; requires a REST server |
| `--no-auto-resume` | | Ask a REST server to refuse implicit resume after a box has run; first boot is unaffected, and the embedded runtime records but does not enforce it |
| `--security <enable\|disable>` | | Embedded-runtime sandbox security (default `enable`; env `BOXLITE_SECURITY`); REST servers own this policy |

`-p` is explicit local publication. Remote REST contexts reject it and direct
the caller to `boxlite network tunnel`.

**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 create --rootfs /path/to/rootfs --name local-rootfs
boxlite create --cap-drop NET_RAW --name hardened alpine:latest
boxlite start mybox
boxlite start openclaw
```

### `boxlite exec`

Run a command in a box. A safely resumable stopped box starts implicitly; a
job box whose start would run or re-run its user-selected main command is
refused, so start that box deliberately with `boxlite start` first.

**Usage:** `boxlite exec [OPTIONS] BOX -- COMMAND [ARGS]...`

| Option | Short | Description |
|--------|-------|-------------|
| `--interactive` | `-i` | Keep STDIN open; conflicts with `--detach` |
| `--tty` | `-t` | Allocate a TTY; foreground mode requires TTY-attached stdin, while detached mode attaches no stdin |
| `--env KEY[=VALUE]` | `-e` | Environment variables; a bare key inherits its host value |
| `--workdir PATH` | `-w` | Working directory |
| `--user NAME[:GROUP]` | `-u` | Run as a name/uid and optional group/gid |
| `--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; conflicts with `--format` |
| `--format FMT` | | Output format: `table`, `json`, `yaml`; conflicts with `--quiet` (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. |

`Network` holds both directions of the box's policy, `Outbound` and `Inbound`,
each with `Mode` (`enabled` or `disabled`) and `AllowNet`. It is `null` when the
runtime does not know the policy, such as a REST server that predates it.

**Examples:**

```bash
boxlite inspect mybox
boxlite inspect -f '{{.State.Status}}' mybox
boxlite inspect -f '{{.Network.Inbound.Mode}}' mybox
boxlite inspect --latest -f yaml
boxlite inspect box1 box2 -f json
```

### `boxlite images`

List cached images.

**Usage:** `boxlite images [OPTIONS]`

| Option | Short | Description |
|--------|-------|-------------|
| `--quiet` | `-q` | Show only image IDs; conflicts with `--format` |
| `--format FMT` | | Output format: `table`, `json`, `yaml`; conflicts with `--quiet` |

### `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`). End a directory
  `SRC` with `/.` to copy its contents rather than the directory itself, as `docker cp` does.

| Option | Description |
|--------|-------------|
| `--follow-symlinks` | Follow symlinks when copying (local runtime only) |
| `--no-overwrite` | Do not overwrite existing files (local runtime only) |

**Examples:**

```bash
boxlite cp ./local.txt mybox:/workspace/
boxlite cp mybox:/app/out ./output
boxlite cp ./dist/. mybox:/srv/www        # the contents of ./dist, not dist/ itself
```

Paths at or under a mount inside the box are refused in both directions — `/tmp`,
`/dev/shm`, volumes, and the `/etc/hosts`, `/etc/hostname`, `/etc/resolv.conf` binds.
`cp` works on the rootfs layer from outside the box's mount namespace, so such a
copy would write where nothing in the box can see it, or read back the image's file
rather than the one the box has.

A directory that merely *contains* a mount differs by direction: copying one **out** is
refused (`boxlite cp box:/etc ./etc`), while copying **in** is allowed and refused only
if a file would land on a mount — so `boxlite cp ./x box:/etc` works.

Copy a path outside the mount, or pipe a tar through
`boxlite exec`. Files copied in are owned by the box's exec user.

### `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, credential, and context preference directory (default: `~/.boxlite`). Overridden by `--home`. |
| `BOXLITE_REST_URL` | REST server endpoint; equivalent to `--url`. |
| `BOXLITE_API_KEY` | Long-lived API key sent as `Authorization: Bearer`. Overrides any stored credentials. |
| `BOXLITE_CONTEXT` | Override the saved current context; explicit `--context` takes precedence. |
| `BOXLITE_PROFILE` | Legacy fallback when `BOXLITE_CONTEXT` is unset or empty. |
| `BOXLITE_REST_PATH_PREFIX` | REST routing path segment; equivalent to `--path-prefix`. |
| `BOXLITE_SECURITY` | `run`/`create` sandbox preset; equivalent to `--security`. |
| `BOXLITE_SERVE_API_KEY` | Expected bearer key for `serve`; equivalent to `serve --api-key`. |
| `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_AUDIENCE` | OIDC login fallbacks after flags and server discovery. |
| `BOXLITE_LOG_FILE` | Explicit JSON log-file path for any command; `serve` otherwise defaults to `<BOXLITE_HOME>/logs/serve.log`. |
| `BOXLITE_EXPERIMENTAL` | Comma-separated RC features: `custom-kernel`, `nested-virtualization`. |
| `BOXLITE_RECONNECT_GRACE` | `serve` reconnect grace (default `5m`). |
| `BOXLITE_SHUTDOWN_GRACE` | `serve` shutdown-stage grace (default `30s`). |
| `BOXLITE_MAX_SESSION_LIFETIME` | `serve` session hard cap (default `24h`). |
| `SSH_CONNECTION` | `auth login` headless detection; choosing Browser from the TTY picker falls back to device flow when set. |
| `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.