CLI
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 commands without booting anything. With a PROFILE argument, builds just that profile (and errors with provium prepare: profile \if it isn't declared); with no argument, builds every profile that declares abuild` 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 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—---@metastubs covering the test framework, theTestContext(t:assert_eq,t:fail, …), the rootLab, and thejsonglobal. Less-trafficked methods are typed asany— 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:
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). 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:
- Discovery: walk
<paths>for*.test.luafiles. --filtersubstring match against test-root-relative path.--rerun-failedintersect with the prior failed set (zero-result clean exit if no prior state).--sincemtime newer than the reference path.- Per-test (within each file):
meta.skip→ instant skip. - Per-test:
meta.slowand--include-slow. - Per-test:
--no-tag(skip if any match). - Per-test:
--tag(run if any match). - Per-test:
--no-tag-meta KEY=VALUE(skip ifmeta[KEY]contains VALUE). - 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 — config-file fields.
- Events — wire format of the msgpack event stream.
- Running tests — patterns for the CLI.