# CLI

_Provium / Reference_

> Every flag, subcommand, environment variable, and exit code for the `provium` binary.

Provium's CLI surface is the single binary `provium`. By default it discovers `*.test.lua` files under the given paths and runs them; subcommands cover REPL, fixture management, and listing.

## Synopsis

```
provium [PATHS]... [FLAGS]
provium prepare [PROFILE]
provium repl <PROFILE> [--name <NAME>] [--fixture <PATH>]
provium console <PROFILE> [--mem <BYTES>] [--cpus <N>] [--cmdline <TEXT>] [--agent] [--qemu <PATH>] [--print-command] [-- QEMU_ARG...]
provium fixture list
provium fixture build <PATH>
provium fixture rebuild <PATH>
provium fixture clean
provium fixture stale
provium list [--fixtures]
provium lsp-setup [DIR] [--force]
```

If no paths are given, the current directory is scanned recursively.

## Top-level flags

### Discovery and selection

| Flag | Type | Default | Description |
|---|---|---|---|
| `--config <PATH>` | path | `provium.toml` | Config file location. |
| `--filter <STR>` | string | none | Only run files whose test-root-relative path contains this substring. |
| `--include-slow` | flag | off | Include `meta.slow` tests (default skips them). |
| `--tag <TAG>` | string, repeatable | none | Run only tests with one of these tags. OR'd. |
| `--no-tag <TAG>` | string, repeatable | none | Skip tests with any of these tags. Wins over `--tag`. |
| `--tag-meta <KEY=VALUE>` | string, repeatable | none | Run only tests where `meta[KEY]` contains `VALUE`. Multi-flag same KEY = OR within key; different KEYs = AND across keys. Useful for filtering on arbitrary fields like `subsystems`. |
| `--no-tag-meta <KEY=VALUE>` | string, repeatable | none | Skip tests where `meta[KEY]` contains `VALUE`. Same key/value semantics as `--tag-meta`. Wins over `--tag-meta`. |
| `--rerun-failed` | flag | off | Run only files that failed in the last run. Reads from `~/.cache/provium/rerun.json`. |
| `--since <PATH>` | path | none | Run only files whose mtime is newer than this reference file. |
| `--watch` | flag | off | Re-run on file change (poll every 500 ms). |

### VMM and resources

| Flag | Type | Default | Description |
|---|---|---|---|
| `--vmm <CHOICE>` | `qemu` / `local` | `qemu` | VMM backend. `local` is for dev runs without KVM — does not actually boot a kernel. |
| `--mem <BYTES>` | size string `"4G"` | 80 % of host RAM | Pool memory budget. |
| `--cpus <N>` | int | host online CPUs | Pool vCPU budget. |
| `--cpu-overcommit <F>` | float | `1.0` | Multiplier on `--cpus`. Clamped to [0.5, 8.0]. |
| `--no-preflight` | flag | off | Skip the startup `/dev/kvm` / iproute2 / nft / qemu / `CAP_NET_ADMIN` checks. |
| `--no-ksm` | flag | off | Skip Kernel Same-page Merging tuning at startup. |
| `--no-build` | flag | off | Skip every dynamic profile's `build` command for this run. Use when you know the artifacts are already current. Does not affect `provium prepare`, whose whole purpose is to build. |

### Lifecycle

| Flag | Type | Default | Description |
|---|---|---|---|
| `--timeout <DUR>` | seconds-int or duration string | `300` (5 min) | Per-file wall-clock timeout. `0` disables. Accepts `"500ms"`, `"30s"`, `"10m"`, `"2h"`. |
| `--fail-fast` | flag | off | Stop after the first failed file. |

### Output

| Flag | Type | Default | Description |
|---|---|---|---|
| `-v`, `--verbose` | flag | off | Show passing tests too. Mutually exclusive with `--quiet`. |
| `-q`, `--quiet` | flag | off | Show only failures. Mutually exclusive with `--verbose`. |
| `--json` | flag | off | Line-delimited JSON output, one object per file. Mutually exclusive with `--events-stdout`. |

### Observability

| Flag | Type | Description |
|---|---|---|
| `--save-events <PATH>` | path | Persist the event stream to PATH as length-prefixed msgpack frames. Compatible with `provium-coverage --from PATH`. |
| `--events-stdout` | flag | Emit msgpack event frames on stdout; the human/JSON renderer redirects to stderr. Mutually exclusive with `--json`. |
| `--events-socket <PATH>` | path | Multiplex the event stream over a Unix socket. The binary listens, accepts connections, fans out frames. Reused across `--watch` iterations. |
| `--coverage` | flag | Pipe the buffered event stream into `provium-coverage` (must be on `PATH`) after the run. |

## Subcommands

When a subcommand is given, the test-runner mode is suppressed.

### `provium prepare [PROFILE]`

Run dynamic profiles' [`build`](/provium/configuration/provium-toml.md#build) commands without booting anything. With a `PROFILE` argument, builds just that profile (and errors with `provium prepare: profile \`<name>\` not found in provium.toml` if it isn't declared); with no argument, builds every profile that declares a `build` command, in name order, stopping at the first failure.

Use it to pre-warm artifacts — `provium prepare && provium --no-build` builds once, then runs the suite without rebuilding — or to drive an image build from a machine that isn't a test host: `prepare` skips the pre-flight checks (`/dev/kvm`, iproute2, `CAP_NET_ADMIN`), so it runs anywhere `sh` and the builder do.

A profile with no `build` command is a silent no-op. A build that exits non-zero aborts with the profile name and exit status, and Provium never falls through to using whatever stale artifacts are on disk.

### `provium repl <PROFILE> [--name <NAME>] [--fixture <PATH>]`

Boot a VM and drop into an interactive Lua REPL against it.

| Arg / flag | Description |
|---|---|
| `<PROFILE>` | Profile name from `provium.toml`. Optional when `--fixture` is given. |
| `--name <NAME>` | VM name. Defaults to `repl`. |
| `--fixture <PATH>` | Resume from a fixture instead of cold-booting. |

If `--fixture` is set without a profile arg, the first profile in `provium.toml` (sorted by name) is used.

### `provium console <PROFILE> [flags] [-- QEMU_ARG...]`

Boot a profile's VM and attach your terminal to its serial console — no agent, no Lua. `Ctrl-A X` quits QEMU; `Ctrl-A C` toggles the QEMU monitor.

| Flag | Description |
|---|---|
| `--mem <BYTES>` | Override the per-VM memory default. |
| `--cpus <N>` | Override the per-VM vCPU default. |
| `--cmdline <TEXT>` | Override the profile's kernel command line. |
| `--agent` | Inject the agent overlay (off by default for console sessions). |
| `--qemu <PATH>` | Pick the QEMU binary. |
| `--print-command` | Print the assembled QEMU command line and exit. |

Anything after `--` is passed verbatim to QEMU.

### `provium fixture list`

Show every cached entry with size and key.

### `provium fixture build <PATH>`

Force a build for the named fixture (test-root-relative path, no `.fixture.lua` suffix). If the fixture is already cached, prints `already built: <path>` and exits.

### `provium fixture rebuild <PATH>`

Evict the existing cache entry and rebuild. Both single-VM (`.snap`) and lab (`.lab/`) layouts are evicted.

### `provium fixture clean`

Wipe the entire cache directory.

### `provium fixture stale`

List `.fixture.lua` files whose source hash (folded with kernel + initrd identifiers and any [external host-file deps](/provium/running-tests/fixtures-and-dependencies.md#external-host-file-deps) declared via `vm:push_file` / `lab:depends_on_file`) doesn't correspond to any cached entry. Useful for "what would `provium` rebuild on the next run?"

### `provium list [--fixtures]`

List discovered tests (default) or fixtures (`--fixtures`) without running anything. Useful for piping into `xargs` or for CI dry-runs.

### `provium lsp-setup [DIR] [--force]`

Drop Lua Language Server definitions into a test directory so `test`, `provium`, `wait_until`, `json`, and the rest of the harness globals stop showing up as undefined in your editor.

Writes:

- `<DIR>/.provium-meta/types.lua` — `---@meta` stubs covering the test framework, the `TestContext` (`t:assert_eq`, `t:fail`, …), the root `Lab`, and the `json` global. Less-trafficked methods are typed as `any` — the goal is silencing diagnostics, not full IDE intellisense.
- `<DIR>/.luarc.json` — config pointing the LSP at `.provium-meta/`.

`DIR` defaults to the current directory. If `.luarc.json` already exists, the command refuses to overwrite and prints the snippet to merge in by hand; pass `--force` to replace it.

Skips pre-flight checks (`/dev/kvm`, iproute2, nft) so it runs cleanly on machines without test prerequisites — useful from a dev laptop separate from the test host.

After running, reload your editor's Lua server. To extend the types further (e.g. richer completion on a specific userdata you use a lot), edit `.provium-meta/types.lua` directly.

## Exit codes

| Code | Meaning |
|---|---|
| `0` | Every file passed (or skipped). |
| `1` | At least one file did not finish cleanly (failed test, panicked runner, timed-out file). With `--coverage`, also reflects the coverage post-run's exit code if it failed. |
| `2` | Internal error — config load failure, pre-flight failure, a dynamic profile's `build` command failing, or a panic in the dispatcher. |

The exit code is intentionally clamped to `0/1/2` rather than reflecting the number of failed files, so shell scripts don't have to worry about overflow at the 256-file mark or accidentally interpret `failed_count == 2` as the internal-error sentinel.

## Environment variables

| Variable | Effect |
|---|---|
| `PROVIUM_TEST_FILTER` | JSON object set by the binary at startup carrying `--include-slow` / `--tag` / `--no-tag` / `--tag-meta` / `--no-tag-meta` so the runner picks them up. Test code does not set this directly. |
| `PROVIUM_OVERLAY` | Path to the agent-overlay cpio. Overrides the binary-relative path-walk; lower priority than the per-profile `agent_overlay_path` config field. Useful for distribution-installed Provium where the overlay isn't co-located with the binary. |
| `PROVIUM_RERUN_STATE` | Override the path of the rerun-state file (default: `~/.cache/provium/rerun.json`, or `/tmp/provium-rerun.json` if `$HOME` is unset). |
| `PROVIUM_COVERAGE_TMP` | Set by `--coverage` to point at the temp event file the post-run hook will read. Cleared after the hook completes. |
| `PROVIUM_COVERAGE_USER_FILE` | Set when `--coverage` reuses an explicit `--save-events` path so the post-run hook does NOT delete it. |
| `PROVIUM_COVERAGE_MARKER` | Marker file `--coverage` uses to recognise its own temp file (vs an externally-set `PROVIUM_COVERAGE_TMP` pointing at user data). |
| `HOME` | Used to compute the default rerun-state path. |
| `PATH` | Searched for `qemu-system-x86_64`, `ip`, `tc`, `nft`, `tcpdump`, and (on `--coverage`) `provium-coverage`. |

## Output shape

### Plain text (default)

```
PASS tests/smoke.test.lua (3 passed, 0 failed, 0 skipped)
FAIL tests/networking/partition.test.lua (2 passed, 1 failed, 0 skipped)
    FAIL split-brain
        client A could not reach client B after partition
TIME tests/long.test.lua (1 passed, 0 failed, 0 skipped)

3 file(s); 6 passed, 1 failed, 0 skipped; 12.34s
```

`-v` adds `PASS <name>` / `SKIP <name>` lines for each test. `-q` suppresses everything except the file-status line for files with failures and the summary.

### JSON (`--json`)

One object per file, line-delimited. Schema:

```json
{
  "path": "tests/smoke.test.lua",
  "timeout": "in_time",
  "passed": true,
  "chunk_error": null,
  "tests": [
    {"name": "boots", "status": "passed", "message": null, "log": []},
    {"name": "fails", "status": "failed", "message": "1 ~= 2", "log": []}
  ]
}
```

`status` is one of `"passed"`, `"failed"`, `"skipped"`. `timeout` is `"in_time"` or `"timed_out"`.

### Msgpack events (`--events-stdout`)

Length-prefixed msgpack frames, one per `EventFrame` (see [events](/provium/reference/events.md)). When `--events-stdout` is set, the human-readable / JSON renderer redirects to stderr so consumers piping `provium --events-stdout | provium-coverage` don't see human text interleaved into their msgpack parser.

## Filter precedence

Multiple selection flags compose:

1. Discovery: walk `<paths>` for `*.test.lua` files.
2. `--filter` substring match against test-root-relative path.
3. `--rerun-failed` intersect with the prior failed set (zero-result clean exit if no prior state).
4. `--since` mtime newer than the reference path.
5. Per-test (within each file): `meta.skip` → instant skip.
6. Per-test: `meta.slow` and `--include-slow`.
7. Per-test: `--no-tag` (skip if any match).
8. Per-test: `--tag` (run if any match).
9. Per-test: `--no-tag-meta KEY=VALUE` (skip if `meta[KEY]` contains VALUE).
10. Per-test: `--tag-meta KEY=VALUE` (run if all KEYs have at least one matching VALUE).

`--watch` mode rediscovers from scratch every iteration; `--rerun-failed` is automatically dropped under `--watch` so file-edit detection works as expected.

## See also

- [provium.toml reference](/provium/configuration/provium-toml.md) — config-file fields.
- [Events](/provium/reference/events.md) — wire format of the msgpack event stream.
- [Running tests](/provium/running-tests/the-cli.md) — patterns for the CLI.

Related content:

- [Running tests with the CLI](/provium/running-tests/the-cli.md)
- [provium.toml](/provium/configuration/provium-toml.md)
- [Events](/provium/reference/events.md)
