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

# Box Config

> Declaratively provision a Lager Box's container — USB device permissions, packages, mounts, environment, and more

`lager box config` manages a declarative configuration for a Lager Box's container. You describe what the box should have — USB device permissions (udev rules), apt packages, bind mounts, environment variables, pip/cargo/npm packages, sysctl values — and then `apply` puts it into effect. The configuration persists across container restarts and box updates.

## Syntax

```bash theme={null}
lager box config COMMAND [OPTIONS]
```

## Global Options

| Option       | Description                 |
| ------------ | --------------------------- |
| `--box TEXT` | Lagerbox name or IP address |
| `--help`     | Show help message and exit  |

## How It Works

Editing the config and putting it into effect are two separate steps:

1. **Change the config** — `udev add`, `apt add`, `mount add`, `env set`, etc. These only edit the stored config; nothing happens on the box yet.
2. **Apply it** — `lager box config apply` validates the config and restarts ("bounces") the container so the changes take effect. Host-side pieces (apt packages, udev rules, sysctl) are installed on the box host during apply; everything else is mounted into the fresh container.

```bash theme={null}
lager box config udev add 1209:0001 --box my-lager-box   # 1. edit
lager box config apply --box my-lager-box                # 2. apply
```

## Commands

**Lifecycle**

| Command             | Description                                                         |
| ------------------- | ------------------------------------------------------------------- |
| `show`              | Print the current config                                            |
| `status`            | One-line summary of config state                                    |
| `diff`              | Show pending changes vs. the last applied config                    |
| `validate`          | Validate the current config                                         |
| `apply`             | Validate, then restart the container so the new config takes effect |
| `init`              | Create the config with defaults                                     |
| `reset`             | Erase the config to empty                                           |
| `restart`           | Restart the container without changing the config                   |
| `repair`            | Restore the config from the last applied snapshot and restart       |
| `edit`              | Open the config in `$EDITOR`                                        |
| `import` / `export` | Replace the config from / write the config to a local JSON file     |
| `copy`              | Copy one box's config to another box                                |
| `audit`             | Show recent config changes recorded on the box                      |

**Provisioning**

| Group                   | Description                                             |
| ----------------------- | ------------------------------------------------------- |
| `udev`                  | Host udev rules granting USB device access (by vid:pid) |
| `apt`                   | Host-side apt packages                                  |
| `mount`                 | Host-to-container bind mounts                           |
| `volume`                | Named docker volumes attached to the container          |
| `env`                   | Container environment variables                         |
| `pip` / `cargo` / `npm` | In-container language packages                          |
| `sysctl`                | Host sysctl values persisted across reboots             |

***

## Command Reference

### `udev`

Grant a USB device read/write access from inside the container, by USB vendor/product id. Use this when a freshly-plugged device is owned by `root` and a tool inside the container can't open it (for example `dfu-util` failing with *"No DFU capable USB device available"*).

```bash theme={null}
lager box config udev add VID:PID [VID:PID ...] [--mode 0666] [--usbtmc]
lager box config udev list [--json]
lager box config udev remove VID:PID [VID:PID ...]
```

| Option        | Description                                                                                                 |
| ------------- | ----------------------------------------------------------------------------------------------------------- |
| `--mode TEXT` | Octal device-node permission mode (default `0666`)                                                          |
| `--usbtmc`    | Also emit the `usbtmc` driver-unbind rule, required for SCPI/USB‑TMC instruments accessed via PyVISA/libusb |

VID and PID are 4 hex digits each. A `0x` prefix and uppercase are accepted and normalized (so `0x1AB1:0E11` becomes `1ab1:0e11`). Re-adding the same vid:pid updates it in place.

```bash theme={null}
# Let dfu-util open a generic test device, then apply
lager box config udev add 1209:0001 --box my-lager-box
lager box config apply --box my-lager-box

# A SCPI power supply that also needs the usbtmc driver unbound
lager box config udev add 1ab1:0e11 --usbtmc --box my-lager-box
lager box config apply --box my-lager-box
```

On `apply`, the rules are installed to `/etc/udev/rules.d/99-lager-user.rules` on the box host and udev is reloaded, so existing devices pick up the new permissions.

### `reset`

Erase the config to a truly empty state. Unlike `init` (which re-seeds the default `box-tools` volume), `reset` clears everything — a clean slate.

```bash theme={null}
lager box config reset [--yes] [--apply]
```

| Option    | Description                                                                |
| --------- | -------------------------------------------------------------------------- |
| `--yes`   | Skip the confirmation prompt                                               |
| `--apply` | Also restart the container so you get a fresh, empty container in one step |

```bash theme={null}
# Wipe the config and bring up a fresh container
lager box config reset --apply --yes --box my-lager-box
```

### `restart`

Restart the container without changing the config — a fresh container with the same setup. Useful for test isolation between runs. Unlike `apply`, it restarts unconditionally (it does not skip when the config is unchanged).

```bash theme={null}
lager box config restart [--yes] --box my-lager-box
```

### `apply`

Validate the config and restart the container so changes take effect.

```bash theme={null}
lager box config apply [OPTIONS] --box my-lager-box
```

| Option              | Description                                                                                                      |
| ------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `--yes`             | Skip the confirmation prompt                                                                                     |
| `--force`           | Restart even if the config is unchanged                                                                          |
| `--dry-run`         | Show what would change, but make no changes                                                                      |
| `--skip-restart`    | Validate and record the config without restarting                                                                |
| `--no-auto-prep`    | Skip host-path re-verification before restart                                                                    |
| `--recursive-chown` | For any configured mount whose host path is wrong-owned and populated, recursively chown it to uid 33 (www-data) |

`--box` accepts a comma-separated list to apply across multiple boxes.

### `apt`

Host-side apt packages (installed on the box host during `apply`).

```bash theme={null}
lager box config apt add usbutils dfu-util --box my-lager-box
lager box config apt list [--json]
lager box config apt remove dfu-util
```

### `mount`

Bind-mount a host path into the container.

```bash theme={null}
lager box config mount add HOST_PATH CONTAINER_PATH [--readonly] --box my-lager-box
lager box config mount list [--json]
lager box config mount remove HOST_PATH CONTAINER_PATH [--yes]
```

`mount add` options:

| Option              | Description                                                                                                        |
| ------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `--readonly`        | Mount as read-only                                                                                                 |
| `--no-auto-prep`    | Skip the auto mkdir/chown of the host path (use when the directory is provisioned externally)                      |
| `--recursive-chown` | If the host path already exists with the wrong owner and contains files, recursively chown it to uid 33 (www-data) |

### `env`

Container environment variables.

```bash theme={null}
lager box config env set KEY=VALUE [KEY=VALUE ...] --box my-lager-box
lager box config env list [--json]
lager box config env unset KEY [KEY ...]
```

### `pip` / `cargo` / `npm`

In-container language packages, installed when the container starts.

```bash theme={null}
lager box config pip add requests rich --box my-lager-box
lager box config cargo add ripgrep --box my-lager-box
lager box config npm add left-pad --box my-lager-box
# each group also supports: list [--json], remove
```

`pip add` validates against PyPI by default; pass `--no-validate-pypi` to skip.

### `sysctl`

Host sysctl values, persisted across reboots.

```bash theme={null}
lager box config sysctl set net.ipv4.ip_forward=1 --box my-lager-box
lager box config sysctl list [--json]
lager box config sysctl unset net.ipv4.ip_forward
```

### `volume`

Named docker volumes attached to the container (persist data across restarts).

```bash theme={null}
lager box config volume add my-vol /opt/my-vol --box my-lager-box
lager box config volume list [--json]
lager box config volume remove my-vol [--yes]
```

### Inspecting and editing

```bash theme={null}
lager box config show --box my-lager-box        # full config
lager box config status --box my-lager-box      # one-line summary (clean / drift)
lager box config diff --box my-lager-box        # pending changes vs. last applied
lager box config validate --box my-lager-box    # check for errors
lager box config audit --box my-lager-box       # recent changes (supports --verb, --since, --tail)
lager box config edit --box my-lager-box        # open in $EDITOR, then apply
```

### Backup, restore, and recovery

```bash theme={null}
lager box config export ./box.json --box my-lager-box     # save current config to a file
lager box config import ./box.json --box my-lager-box      # replace config from a file
lager box config copy --from BOX_A --to BOX_B              # clone config between boxes
lager box config repair --box my-lager-box                 # restore the last applied config and restart
```

***

## Notes

* Most editing commands only change the stored config — run `apply` to put changes into effect.
* udev rules, apt packages, and sysctl values are applied to the box **host**; mounts, env, and pip/cargo/npm apply inside the **container**.
* The config persists across container restarts and box updates. A user udev file (`99-lager-user.rules`) is preserved across `lager update`.
* `--box` accepts a name (from `lager boxes`) or an IP address.
