scripts/nvx.py is the supported command-line entry point for building,
running, benchmarking, and packaging NVX. Run it from the repository root with
Python 3.10 or newer:
python3 scripts/nvx.py COMMAND [OPTIONS]
The examples on this page use the POSIX spelling. On Windows, use
python scripts\nvx.py instead. Every command accepts -h or --help,
including nested commands:
python3 scripts/nvx.py --help
python3 scripts/nvx.py run --help
python3 scripts/nvx.py performance gate --help| Command | Description |
|---|---|
init |
Initialize the OpenVMM submodule and its nested submodules. |
build-guest |
Build the Linux kernel and selected guest artifacts. |
build-kernel |
Build the pinned and patched Linux kernel natively. |
build-initramfs |
Build the selected Alpine or Ubuntu initramfs natively. |
build-distro-layer |
Build a deterministic Ubuntu EROFS distro layer. |
verify-guest-determinism |
Rebuild Ubuntu guest artifacts twice and compare SHA-256 values. |
build-openvmm |
Build the OpenVMM release binary. |
record-openvmm-provenance |
Bind an existing OpenVMM binary to the pinned source revision. |
materialize-kernel-provenance-inputs |
Write kernel provenance inputs from raw run-head blobs. |
setup-cross-os-cache |
Install GNU tar and zstd for GitHub Actions cross-OS caches. |
check-required-ci |
Validate required GitHub Actions job results. |
test-openvmm-unit |
Run the OpenVMM workspace unit and documentation tests. |
test-openvmm |
Run self-contained OpenVMM microVM control-plane tests. |
test-microvm |
Run NVX Linux and device correctness tests through OpenVMM. |
test-adversarial |
Run a brokered Copilot-driven adversarial campaign. |
build |
Build the guest artifacts and OpenVMM. |
download |
Download and install the latest matching GitHub release. |
run |
Run an OpenVMM microVM. |
sandbox |
Run or manage workloads over EROFS layers and private ext4 scratch. |
benchmark |
Run the OpenVMM-native benchmark coordinator. |
performance |
Collect, gate, and persist CI performance results. |
collect-sources |
Materialize verified Linux, Alpine, and Ubuntu release sources. |
collect-alpine-sources |
Collect exact Alpine recipes and upstream sources. |
collect-ubuntu-sources |
Collect exact Ubuntu source packages. |
create-linux-source-archive |
Create a Linux corresponding-source archive. |
package |
Stage a binary distribution. |
archive-release |
Create a deterministic archive from a staged distribution. |
verify |
Verify source and submodule inputs. |
snapshot |
Inspect and validate saved VM snapshots. |
python3 scripts/nvx.py initInitializes and recursively updates the OpenVMM Git submodule.
python3 scripts/nvx.py verifyVerifies the repository's pinned source inputs and submodule state.
python3 scripts/nvx.py setup-cross-os-cacheInstalls the GNU tar and zstd tools used by GitHub Actions cross-OS caches.
See Setup for host prerequisites.
python3 scripts/nvx.py build-guest
[--guest {alpine,ubuntu,all}]
[--native]
By default, builds the guest kernel and initramfs with Docker. --native
builds the selected artifacts directly on Linux instead. Alpine is the
default. --guest all also builds the Ubuntu EROFS distro layer.
python3 scripts/nvx.py build-kernelFetches, verifies, patches, and builds the pinned kernel directly on Linux.
python3 scripts/nvx.py build-initramfs [--guest {alpine,ubuntu}]Builds the selected initramfs directly on Linux. Alpine is the default.
python3 scripts/nvx.py build-distro-layer
--guest ubuntu
[--output PATH]
[--replace]
Builds the immutable Ubuntu EROFS distro layer directly on Linux. The default
output is build/ubuntu-distro.erofs. Existing output or manifest files are
rejected unless --replace is present.
python3 scripts/nvx.py verify-guest-determinism
--guest ubuntu
[--work-dir PATH]
Builds the Ubuntu initramfs and EROFS layer twice from separate roots and compares every artifact SHA-256. A mismatch reports the first differing normalized rootfs entry when one exists.
python3 scripts/nvx.py build-openvmm [--skip-restore] [--backend {kvm,mshv,whp}]
Builds the openvmm release binary. Before building, the command runs
cargo xflowey restore-packages; use --skip-restore when those packages are
already restored. Without --backend, Windows builds the native MSVC target
and Linux builds the native GNU target. On Linux, --backend kvm selects GNU
and --backend mshv selects musl; Windows accepts --backend whp. Build-target
selection does not probe /dev/kvm or /dev/mshv, so compilation also works
on build-only hosts and hosts exposing both devices. Unsupported OS/backend
combinations are rejected.
python3 scripts/nvx.py build
[--guest {alpine,ubuntu,all}]
[--native]
[--skip-restore]
[--backend {kvm,mshv,whp}]
Runs build-guest followed by build-openvmm. The options have the same
meaning as on those individual commands.
See Build for dependencies, outputs, and native build details.
python3 scripts/nvx.py test-openvmm-unitRuns the OpenVMM workspace's unit-test binaries with cargo-nextest's agent
profile and the ci feature. Packages that require specialized test harnesses
are excluded, along with all fuzz crates reported by OpenVMM's xtask.
Afterward, runs the workspace doctests with Cargo.
python3 scripts/nvx.py test-openvmm --backend {kvm,mshv,whp}
Builds and runs OpenVMM's checkout-owned VMM tests. The Linux-direct microVM
TTRPC test boots NVX's build/vmlinux and build/initramfs.cpio.gz, so build
the guest first; the remaining test artifacts are produced by OpenVMM itself.
python3 scripts/nvx.py test-microvm
--backend {kvm,mshv,whp}
[--guest {alpine,ubuntu}]
[--scenario SCENARIO]...
[--processors {1,2,4,8} ...]
[--memory-mib MIB]
[--timeout SECONDS]
[--output-dir PATH]
Runs NVX-owned Linux, SMP, virtio, sandbox, and snapshot correctness scenarios
against the public OpenVMM CLI. Repeat --scenario to select a subset; without
it, every scenario supported by the selected guest runs. Alpine remains the
default. Ubuntu rejects the Alpine-control-only sandbox-blocks and
scratch-snapshot scenarios, the Alpine-prompt-specific console-snapshot
scenario, and the sandbox-control-dependent snapshot-tiers scenario. The
command requires build/vmlinux, the selected initramfs, and
openvmm/target/release/openvmm[.exe].
python3 scripts/nvx.py test-adversarial
--backend {kvm,mshv,whp}
--campaign {workload-isolation,guest-isolation,snapshot-isolation}
[--budget-seconds SECONDS]
[--budget-actions COUNT]
[--budget-ai-credits CREDITS]
[--seed SEED]
[--output-dir PATH]
[--replay ACTIONS.jsonl]
[--model MODEL]
[--host-type {baremetal,virtual-machine}]
[--memory-mib MIB]
[--phase-timeout SECONDS]
[--action-timeout SECONDS]
[--executor-command PATH]
[--no-minimize]
[--minimize-attempts COUNT]
Runs a bounded adaptive campaign in which an already installed and authenticated Copilot CLI selects one deterministic primitive at a time. Copilot has no tools or direct NVX access. The typed broker validates and records every action, while a credential-free executor and independent watchdog own VM operation, canaries, teardown checks, and the clean post-campaign boot.
| Option | Default | Description |
|---|---|---|
--backend |
required | Select KVM or MSHV on Linux, or WHP on Windows. |
--campaign |
required | Select workload, privileged-guest, or snapshot boundary probes. |
--budget-seconds |
900 |
Bound preflight, actions, canary boot, and minimization wall time. |
--budget-actions |
8 |
Bound accepted and executed broker actions. |
--budget-ai-credits |
300 |
Bound charged Copilot usage; the minimum is 60 credits so authentication preflight and at least one action each retain a 30-credit CLI cap. |
--seed |
0 |
Seed deterministic candidate ordering and synthetic canary data. |
--output-dir |
backend-specific path under build/test-results |
Parent for a unique campaign run directory. |
--replay |
none | Replay an actions.jsonl bound to its sibling replay-manifest.json, without invoking Copilot. |
--model |
Copilot auto routing | Select the strategist model. |
--host-type |
NVX_HOST_TYPE or unspecified |
Record whether the executor is bare metal or a virtual machine. |
--memory-mib |
256 |
Set memory for deterministic microVM scenarios. |
--phase-timeout |
60 |
Set each underlying deterministic scenario phase timeout. |
--action-timeout |
600 |
Set the outer limit for one action or canary boot. |
--executor-command |
local child executor | Select one trusted no-argument wrapper for a separate disposable target. |
--no-minimize |
off | Disable fresh-target shorter-prefix replay after an anomaly. |
--minimize-attempts |
3 |
Bound shorter-prefix attempts within the campaign time budget. |
Missing or unauthenticated Copilot CLI is a preflight failure in adaptive
mode. Replay mode has no Copilot prerequisite. Production and CI campaigns
must use a separate disposable executor through --executor-command; local
mode cannot reliably classify a target-host crash. Local target logs are kept
under the short build/adv state root to avoid Windows path-length failures;
the run summary records their absolute location. See
Copilot-driven adversarial testing
for the trust boundary, wrapper contract, artifacts, and CI policy.
python3 scripts/nvx.py download
[--repository OWNER/REPOSITORY]
[--hypervisor {auto,whp,kvm,mshv}]
| Option | Default | Description |
|---|---|---|
--repository OWNER/REPOSITORY |
microsoft/nvx |
GitHub repository from which to download the latest release. |
--hypervisor {auto,whp,kvm,mshv} |
auto |
Select the release platform. auto chooses WHP on Windows and KVM on Linux. |
Windows release downloads support WHP. Linux release downloads support KVM
and MSHV. download first uses GH_TOKEN or GITHUB_TOKEN when configured.
If GitHub rejects that token with HTTP 401 or 403, NVX reports the failure and
retries without credentials so public releases remain downloadable. Private
repositories require a token with read access to the repository contents that
is authorized for the organization when it enforces single sign-on.
python3 scripts/nvx.py run
[--guest {alpine,ubuntu}]
[--hypervisor {auto,whp,kvm,mshv,hvf}]
[--machine {microvm}]
[--memory-mib MIB]
[--memory-capacity-mib MIB]
[--memory-backing-file PATH]
[--processors {1,2,4,8}]
[--mount GUEST_TARGET,HOST_PATH[,ro|rw]]
[--mount-deny HOST_PATH]...
[--net IPV4/PREFIX]
[--network-profile {portable}]
[--network-egress {allow,deny}]
[--network-ingress {allow,deny}]
[--network-egress-allow CIDR[:PROTOCOL:PORT]]...
[--network-egress-deny CIDR[:PROTOCOL:PORT]]...
[--host-loopback {allow,deny}]
[--network-proxy IPV4:TCP-PORT]
[--host-loopback-forward PROTOCOL:HOST_PORT:GUEST_PORT]...
[--virtio-net BACKEND]...
[--share PORT:MNTPOINT[:ro|rw]]...
[--serve HOSTDIR:MNTPOINT[:ro|rw]]...
[--kernel PATH]
[--initrd PATH]
[--disk [PATH]]
[--disk-mount MNTPOINT]
[--outcome-report PATH]
[--cmdline TEXT]
[--save-snapshot DIR]
[--save-on MARKER]
[--save-exec CMD]
[--save-ready MARKER]
[--save-timeout SECONDS]
[--restore-snapshot PATH]
[--restore-processors {1,2,4,8}]
[--restore-memory-mib MIB]
[--restore-ready-path PATH]
[--dry-run]
| Option | Default | Description |
|---|---|---|
--guest {alpine,ubuntu} |
alpine |
Select Alpine or Ubuntu userland with the same NVX kernel. This option is not used for snapshot restore. |
--hypervisor {auto,whp,kvm,mshv} |
auto |
Select the OpenVMM hypervisor. auto chooses WHP on Windows and KVM elsewhere. |
--machine {microvm} |
microvm |
Select the fixed-topology microVM with shared-status edge interrupts. |
--memory-mib MIB |
guest-specific | Set guest memory in MiB. Defaults to 128 for Alpine and 256 for Ubuntu. |
--memory-capacity-mib MIB |
none | Reserve an immutable, 128 MiB-aligned RAM capacity for a fresh microVM snapshot. |
--processors {1,2,4,8} |
1 |
Select the microVM processor count. |
--mount GUEST_TARGET,HOST_PATH[,ro|rw] |
none | Expose one host directory to the absolute guest target. Active snapshot restore requires the same canonical path, target, and mode; a dormant-slot restore may attach a new mapping that the resumed guest mounts explicitly. |
--mount-deny HOST_PATH |
none | Hide one existing file or directory inside the mounted host root; repeat to deny multiple paths. |
--net IPV4/PREFIX |
none | Enable virtio-net with the static guest IPv4 address and prefix. |
--network-profile {portable} |
none | Select the required cross-platform network behavior contract; must be specified with --net. |
--network-egress {allow,deny} |
allow |
Set the default guest egress policy. |
--network-ingress {allow,deny} |
deny |
Set the default host ingress policy. The portable profile currently supports only deny; allow is rejected before launch. |
--network-egress-allow CIDR[:PROTOCOL:PORT] |
none | Allow matching guest egress; repeat to add rules. |
--network-egress-deny CIDR[:PROTOCOL:PORT] |
none | Deny matching guest egress; repeat to add rules. Deny rules take precedence. |
--host-loopback {allow,deny} |
existing mapping | Control guest access to host loopback services. |
--network-proxy IPV4:TCP-PORT |
none | Allow one explicit host TCP proxy endpoint. |
--host-loopback-forward PROTOCOL:HOST_PORT:GUEST_PORT |
none | Publish one TCP or UDP localhost port to the guest; repeat to add forwards. |
--virtio-net BACKEND |
none | Expose a virtio NIC (e.g. consomme or consomme:192.168.127.0/24); repeat to add NICs. On hvf the guest configures it via DHCP unless an egress policy selects a static identity. |
| `--share PORT:MNTPOINT[:ro | rw]` | none |
| `--serve HOSTDIR:MNTPOINT[:ro | rw]` | none |
--kernel PATH |
built artifact | Custom direct-boot kernel (fresh boot only). |
--initrd PATH |
built artifact | Custom initramfs (fresh boot only). |
--disk [PATH] |
none | Persistent virtio-blk disk (hvf only); a missing image is created sparse (4 GiB). Bare --disk uses nvx-disk.raw in the repo root. Repeat the same --disk on restore. |
--disk-mount MNTPOINT |
/data |
Guest mountpoint for --disk (fresh boot; baked into the snapshot on restore). |
--outcome-report PATH |
none | Write a bounded local JSON outcome report. |
--cmdline TEXT |
empty | Append kernel parameters; nvx_* and tsc= tokens are reserved. |
--memory-backing-file PATH |
none | Fresh hvf boot only: file-backed guest RAM, required for --save-snapshot scripted capture (the NIC snapshot option is added automatically). |
--save-snapshot DIR |
none | Scripted capture: wait for --save-on, run --save-exec, then send snap DIR to the openvmm REPL (Ctrl-Q + probe sync), wait for snapshot saved, shut down. The leaf must not exist; when the parent is a symlink, pass the real path (/private/tmp/x, not /tmp/x on macOS). |
--save-on MARKER |
none | Output marker that starts --save-snapshot (e.g. VIRTDISK-OK). |
--save-exec CMD |
none | Guest shell line after --save-on, before capture. Requires --save-ready; needs a console shell (Alpine direct boot). |
--save-ready MARKER |
none | Marker that --save-exec finished; capture starts here. |
--save-timeout SECONDS |
600 |
Per-wait timeout for the save marker and the snapshot-saved marker. |
--restore-snapshot PATH |
none | Restore the immutable machine contract and saved state from a snapshot directory. |
--restore-processors {1,2,4,8} |
none | Bring this contiguous processor prefix online before restore readiness. Requires an opt-in microVM snapshot and cannot exceed --processors capacity. |
--restore-memory-mib MIB |
none | Select the 128 MiB-aligned RAM target for an expansion-capable snapshot restore. |
--restore-ready-path PATH |
none | Publish one restore-readiness event to an existing Unix socket or Windows named pipe. |
--dry-run |
off | Print the generated OpenVMM command without running it. |
The command requires the OpenVMM release binary, build/vmlinux, and the
selected build/initramfs*.cpio.gz. Ubuntu selection never falls back to
Alpine. See Run for host setup, guest shutdown, networking, and
virtio-fs examples.
python3 scripts/nvx.py sandbox
[{run,provision,start,exec,stop,deprovision}]
[--layer ROLE,PATH,EROFS_UUID]...
[--scratch PATH]
[--state-dir PATH]
[--entrypoint PATH]
[--arg VALUE]...
[--hostname NAME]
[--workload-user UID:GID]
[--memory-max BYTES]
[--pids-max COUNT]
[--memory-mib MIB]
[--timeout SECONDS]
[--exec-timeout-ms MILLISECONDS]
[--hypervisor {auto,whp,kvm,mshv}]
[--mount GUEST_TARGET,HOST_PATH[,ro|rw]]
[--mount-deny HOST_PATH]...
[--net IPV4/PREFIX]
[--network-profile {portable}]
[--network-egress {allow,deny}]
[--network-ingress {allow,deny}]
[--network-egress-allow CIDR[:PROTOCOL:PORT]]...
[--network-egress-deny CIDR[:PROTOCOL:PORT]]...
[--host-loopback {allow,deny}]
[--network-proxy IPV4:TCP-PORT]
[--host-loopback-forward PROTOCOL:HOST_PORT:GUEST_PORT]...
[--outcome-report PATH]
[--cmdline TEXT]
[--dry-run]
Network policy and live-share options configure only the run and provision
launches.
| Option | Default | Description |
|---|---|---|
{run,provision,start,exec,stop,deprovision} |
run |
Select a one-shot run or a managed lifecycle operation. |
--layer ROLE,PATH,EROFS_UUID |
required for run and provision |
Attach a distro, runtime, or custom EROFS layer. Repeat once per distinct role. |
--scratch PATH |
required for run and provision |
Attach a preformatted ext4 scratch image as the writable overlay. |
--state-dir PATH |
required for managed operations | Select persistent sandbox state. One-shot run rejects this option. |
--entrypoint PATH |
/bin/sh |
Select an absolute workload entrypoint without whitespace. |
--arg VALUE |
none | Append one whitespace-free entrypoint argument. Repeat to pass multiple arguments. |
--hostname NAME |
nvx-sandbox |
Set the workload UTS hostname. |
--workload-user UID:GID |
65534:65534 |
Select the fixed non-root workload identity for run or provision. |
--memory-max BYTES |
none | Set the workload cgroup memory limit. |
--pids-max COUNT |
none | Set the workload cgroup process limit. |
--memory-mib MIB |
256 |
Set guest memory in MiB. |
--timeout SECONDS |
60 |
Set the control response timeout for managed start, exec, and stop. |
--exec-timeout-ms MILLISECONDS |
0 |
Set the managed exec guest workload timeout; zero disables it. |
--hypervisor {auto,whp,kvm,mshv} |
auto |
Select the host hypervisor. |
--mount GUEST_TARGET,HOST_PATH[,ro|rw] |
none | Live-share one host directory at the absolute target inside the container rootfs for run or provision; defaults to ro. /, /etc, and the /proc, /sys, /dev, and /.nvx-agent trees are reserved. |
--mount-deny HOST_PATH |
none | Hide one existing file or directory inside the --mount host directory; relative paths are resolved inside it. Repeat to deny multiple paths. |
--net IPV4/PREFIX |
none | Enable virtio-net with a static guest address. |
--network-profile {portable} |
none | Select the required cross-platform network behavior contract; must be specified with --net. |
--network-egress {allow,deny} |
allow |
Set the default guest egress policy for run or provision. |
--network-ingress {allow,deny} |
deny |
Set the host ingress policy for run or provision. The portable profile supports only deny. |
--network-egress-allow CIDR[:PROTOCOL:PORT] |
none | Allow matching guest egress; repeat to add rules. Requires explicit --network-egress. |
--network-egress-deny CIDR[:PROTOCOL:PORT] |
none | Deny matching guest egress; repeat to add rules. Requires explicit --network-egress; deny rules take precedence. |
--host-loopback {allow,deny} |
existing mapping | Control guest access to host loopback services for run or provision. |
--network-proxy IPV4:TCP-PORT |
none | Allow one explicit host TCP proxy endpoint; the IPv4 address must match the guest gateway. |
--host-loopback-forward PROTOCOL:HOST_PORT:GUEST_PORT |
none | Publish one TCP or UDP localhost port to the guest; repeat to add forwards and set --host-loopback allow. |
--outcome-report PATH |
none | Write a bounded local JSON outcome report for one-shot run or managed exec. |
--cmdline TEXT |
empty | Append non-sandbox kernel parameters; nvx_*, virtfs_*, and tsc= tokens are reserved. |
--dry-run |
off | Print the generated OpenVMM microVM command without running it. |
See Run for artifact preparation, the security boundary, and current snapshot/configuration limitations.
python3 scripts/nvx.py snapshot verify SNAPSHOT_DIR
| Subcommand | Description |
|---|---|
verify SNAPSHOT_DIR |
Validate a snapshot directory before restoring it: all three artifacts are present as regular files (never symlinks), the manifest parses with a supported version, and the recorded memory/state lengths match the files on disk. Prints the manifest summary (version, architecture, guest RAM, vCPU count, boot mode, device-state root) and snapshot OK. |
See Run for the macOS/HVF save/restore flow, snapshot chaining, and restore-time disk requirements.
python3 scripts/nvx.py benchmark [OPTIONS]
| Option | Default | Description |
|---|---|---|
--suite {boot,snapshot,restore,e2e,phase2,snapshot-profile,all,cold-start,device-io,device-restore-profile,network-snapshot,performance,shell-snapshot,shell-snapshot-restore,snapshot-restore-memory,snapshot-restore-vcpu,virtfs} |
boot |
Select an acceptance, diagnostic, or workload suite. |
--backend {whp,kvm,mshv,both} |
both on Windows; kvm elsewhere |
Select the hypervisor backend. |
--platform NAME |
inferred OS/backend | Record the host-typed performance series. |
--openvmm-dir PATH |
openvmm/ |
Select the OpenVMM repository. |
--nvx-dir PATH |
repository root | Select the NVX repository containing guest artifacts. |
--warmups N |
5 for device-io; 1 for device-restore-profile; 3 otherwise |
Set the number of excluded warmup attempts; zero is allowed. |
--runs N |
30 for device-io; 5 for device-restore-profile; 11 otherwise |
Set the number of retained attempts. |
--memory-mib MIB |
128 |
Set guest memory for the general suites. |
--processors {1,2,4,8} |
1 |
Run every cold, capture, restore, and workload launch with this microVM count. |
--virtfs-runs N |
3 |
Set the number of virtio-fs workload samples. |
--virtfs-memory-mib MIB |
512 |
Set guest memory for the virtio-fs workload. |
--payload-mib MIB |
64 |
Set the virtio-fs sequential I/O payload size. |
--shell-memories MIB [MIB ...] |
128 256 512 (128 256 512 1024 for snapshot-profile) |
Set the guest memory sizes for shell snapshot measurements. |
--network-memory-mib MIB |
256 |
Set guest memory for the network snapshot workload. |
--restore-devices {console,net,virtiofs} [...] |
all three devices | Select devices for the device-restore-profile suite. |
--restore-modes {active,deferred} [...] |
both modes | Select activation modes for the device-restore-profile suite. |
--device-io-duration-seconds SECONDS |
10 |
Set each storage-operation or UDP round-trip measurement window. |
--device-io-size-mib MIB |
512 |
Set the virtio-blk and virtio-fs backing-object size. |
--device-io-port PORT |
5201 |
Set the same-host UDP echo port. |
--net IPV4/PREFIX |
none | Enable virtio-net with a static guest address. |
--network-profile {portable} |
none | Required with --net; selects the portable KVM/MSHV/WHP contract. |
--cpus CPUSET |
one logical CPU per physical core | Set process affinity in taskset syntax. |
--host-cpu-reserve N |
2 |
Require this many affinity CPUs beyond the guest vCPU count for VMM/device work. |
--timeout SECONDS |
10 |
Set the time allowed for each boot marker. |
--teardown-mode {guest-exit,host-terminate,host-sigterm} |
guest-exit |
Select how to stop a measured VM; host-sigterm is a deprecated alias. |
--skip-build |
off | Reuse existing release binaries. |
--snapshot-profile |
off | Retain OpenVMM lifecycle phase samples and host counters; implied by the snapshot-profile suite. |
--cache-state {warm,cold,both} |
both |
Select artifact cache states for the snapshot-profile suite. |
--output PATH |
none | Write the benchmark result as JSON. |
--output-dir PATH |
none | Write canonical workload logs to a directory. |
--scratch-dir PATH |
system temporary directory | Select an existing directory for temporary snapshots, guest RAM backing, and workload files. |
--keep-kvm-stage |
off | Keep temporary staged KVM benchmark binaries. |
Measured counts must be at least 1; warmups may be zero, and timeouts must be greater than zero.
The e2e suite uses the general memory size and measures cold start, snapshot
generation, snapshot restore, guest-exit teardown, and peak RSS against the
shell-ready markers. CI uses the default 128 MiB baseline.
See Benchmark for suite semantics, platform support, metric
definitions, and complete examples.
performance processes benchmark outputs for CI. It requires one nested
command.
python3 scripts/nvx.py performance collect
--platform PLATFORM
--commit COMMIT
--input-dir PATH
--output-dir PATH
[--require-network]
[--require-shell-snapshot]
[--require-shared-suite]
[--require-shell-snapshot-restore-512]
[--lifecycle-input PATH]
[--summary PATH]
Parses canonical benchmark logs into p50 CSV files. The --require-* flags
reject incomplete inputs for their respective workload sets.
--require-shell-snapshot-restore-512 accepts only the canonical 512 MiB
restore metric from a 2-, 4-, or 8-vCPU run.
--lifecycle-input validates and merges a 128 MiB, guest-exit e2e JSON
result, producing the 31-metric microVM CI result. A directory whose metadata
selects device-io is collected as five additional ABI-2, one-vCPU ops/s
metrics; CI merges them into a 36-metric one-vCPU result.
--summary writes the p50 table plus lifecycle min/max/sample-count and RSS diagnostics.
python3 scripts/nvx.py performance validate-openvmm
--platform PLATFORM
--input PATH
Validates a complete 128 MiB, guest-exit OpenVMM e2e result without
writing a CSV. Snapshot-generation instability exits with status 75 so
callers can remeasure the temporary host condition selectively; other
malformed or incomplete inputs exit with status 2.
python3 scripts/nvx.py performance collect-openvmm
--platform PLATFORM
--commit COMMIT
--input PATH
--output-dir PATH
[--summary PATH]
Converts a complete 128 MiB, guest-exit OpenVMM e2e result into an
eight-metric lifecycle p50 CSV.
python3 scripts/nvx.py performance gate
--baseline-dir PATH
--target-dir PATH
[--window N]
[--minimum-history N]
[--threshold PERCENT]
[--absolute-tolerance-ms MILLISECONDS]
[--summary PATH]
Checks target p50 values for regressions against rolling baseline histories.
--window defaults to 10, --minimum-history to 10, --threshold to
40, and --absolute-tolerance-ms to 5. The gate uses the median of the
available window and treats metrics with insufficient history as warmups.
--summary writes a Markdown summary.
python3 scripts/nvx.py performance persist
--source-dir PATH
--history-dir PATH
[--exclude-metric NAME]...
Appends current p50 values to branch history. Repeat --exclude-metric to omit
more than one metric.
python3 scripts/nvx.py collect-sourcesMaterializes the verified Linux, Alpine, and Ubuntu source artifacts needed for a source-inclusive release.
python3 scripts/nvx.py collect-alpine-sources MANIFEST [MANIFEST ...]
[--output PATH]
[--cache PATH]
[--skip-upstream]
| Option | Default | Description |
|---|---|---|
MANIFEST |
required | One or more Alpine package manifests to collect. |
--output PATH |
build/sources/alpine |
Select the output directory. |
--cache PATH |
.cache/aports |
Select the aports cache directory. |
--skip-upstream |
off | Collect exact aports recipes without running abuild fetch. |
python3 scripts/nvx.py collect-ubuntu-sources MANIFEST [MANIFEST ...]
[--output PATH]
[--cache PATH]
| Option | Default | Description |
|---|---|---|
MANIFEST |
required | One or more Ubuntu package or EROFS manifests to collect. |
--output PATH |
build/sources/ubuntu |
Select the output directory. |
--cache PATH |
.cache/ubuntu-source-indexes |
Select the downloaded source-index cache. |
The collector requires gpgv, deduplicates source package name/version pairs,
authenticates live or historical Ubuntu source indexes through signed
InRelease metadata and the pinned Ubuntu archive keyring, validates each
.dsc, and downloads every referenced source member.
python3 scripts/nvx.py create-linux-source-archive
--config PATH
--output PATH
Creates the Linux corresponding-source archive using the required kernel configuration and output paths.
python3 scripts/nvx.py package
[--version VERSION]
[--destination PATH]
(--include-source | --binary-only)
[--force]
| Option | Description |
|---|---|
--version VERSION |
Override the packaged version. |
--destination PATH |
Override the staging destination. |
--include-source |
Include the corresponding source artifacts in the package. |
--binary-only |
Stage binaries only; publish corresponding source separately. |
--force |
Replace a legacy staging directory beneath dist/. Self-contained packages require a new destination. |
Exactly one of --include-source and --binary-only is required. See
Package and source delivery for release procedures and
source-publication requirements.
python3 scripts/nvx.py archive-release
--source PATH
--destination PATH
Validates the staged distribution against its SHA256SUMS, snapshots the
accepted inventory, and creates a deterministic archive at the destination.
The destination must be outside the source directory and end in .tar.gz or
.zip.
| Command or option | Behavior |
|---|---|
doctor [--backend kvm|mshv|whp|hvf] [--json] |
Check artifacts, host hypervisor access, entitlement, Docker, cache, and provenance; any failed check exits nonzero. |
setup |
Apply and verify the macOS hypervisor entitlement with ad-hoc signing for local development. |
explain STATE_DIR |
Explain recorded policy denials and print the specific enabling option. |
run --events PATH |
Write bounded, redacted, host-side JSON-lines lifecycle events. |
sandbox --hypervisor hvf |
Run EROFS layers over a private ext4 overlay on Apple Silicon. Supports managed provision/start/exec/stop. |
sandbox --memory-mib N |
Default RAM is 1024 MiB on HVF and 256 MiB on x86 sandbox backends. |
run --cmdline nvx_host_clock=off |
Disable boot clock repair for the clock negative control. Normal boots sample the current host clock. |
The host resolves snapshot parent paths before passing them to OpenVMM, so the
macOS /tmp alias is accepted. File members of a snapshot are still checked
without following symlinks. See cookbook for complete commands.
The image front door runs through the authenticated managed agent. Conversion happens
before launch, using a networkless Linux tools container; it never executes the image's
entrypoint. image pull contacts the registry through Docker. The manifest records the
resolved OCI config/layer digests and the generated EROFS/scratch digests.
| Command / option | Behavior |
|---|---|
image pull REF / image convert REF |
Pull then convert, or convert an existing Docker image |
--curated-base on conversion |
Register an exact ordered layer-digest prefix as a distro base |
image ls / image verify REF / image rm REF |
List, rehash, or remove an unreferenced image; live/snapshot leases prevent removal |
run --image REF -- COMMAND ARG... |
Use implicit read-only layers and private preformatted scratch; preserves argv and image environment defaults |
--profile default |
UID/GID 65534, empty capabilities, NNP, allowlist seccomp, device filter, masked paths, deny egress, 128 workload PIDs, 256 MiB workload memory and 60 s wall cap |
--profile ci |
Same isolation defaults; permits explicit egress allow rules and --cap-add MKNOD for device-filter testing |
--profile risky |
Root, all capabilities, no seccomp/device/mask/NNP/resource restrictions, allow egress; explicit negative-control profile |
--seccomp unconfined |
Disable seccomp while retaining the other selected-profile restrictions |
--config FILE / policy show / policy lint |
Resolve [policy] in TOML; explicit CLI settings override file settings, which override the selected profile |
--pids-max N / --memory-max BYTES / --exec-timeout-ms MS |
Override workload resource caps; zero wall timeout disables that cap |
verify-guest-determinism --image REF |
Reconvert twice and compare the complete manifest identity; repeat for two or more images |
The high-level image path defaults to portable networking on 192.168.127.0/24 and
records packet decisions. Denied canonical TCP/UDP traffic gets a local TCP reset or
ICMP administrative prohibition. Other malformed/spoofed traffic is dropped. The
NVX_NETWORK_LEGACY_DROP host environment variable is a test control that restores
blackhole behavior; the fast-fail scenario requires that control to time out.
Use numeric IPv4 policy destinations with the existing
--network-egress-allow IPV4:tcp:PORT form. default rejects allow rules; use ci.
A live-share or gateway-loopback opt-in remains an explicit authorization.
nvx-default also filters arguments: clone rejects new user, mount, network,
PID, UTS, IPC and cgroup namespace flags. clone3 returns ENOSYS, allowing libc
to fall back to filtered clone, because classic seccomp cannot inspect its
pointed-to argument structure. socket permits only Unix, IPv4 and IPv6 domains;
netlink and packet sockets return EPERM. A kernel without IPv6 support can still
return EAFNOSUPPORT for that permitted domain. Ordinary forks and threads are
tested alongside the denials.
| Command / option | Behavior |
|---|---|
run --image REF --receipt FILE |
Publish receipt v1 after confirmed teardown; the instance retains its receipt and evidence |
--receipt-signing-key FILE |
Sign canonical receipt body bytes with an Ed25519 PEM private key in process using cryptography |
receipt verify FILE [--evidence-dir DIR] |
Verify body integrity and every referenced local evidence file |
receipt verify FILE --public-key FILE |
Additionally verify an Ed25519 signature against the caller's trusted public key |
containment run --backend BACKEND --format md |
Run all eight probe families; each requires an observed failing negative control |
containment render RESULT.json --format md |
Render a completed, validated result document |
An unsigned digest detects changed content; authenticity requires a signature and a
trusted key. Receipt flow verdicts describe endpoint policy decisions, not remote
application success. Resource numbers come from the outer agent's cgroup counters.
The existing --outcome-report schema and separated exec streams remain available.
Install the optional pinned signing dependency with
python3 -m pip install -r requirements-signing.txt in the NVX installation.
Signing and verification read the supplied PEM key but create no temporary message,
signature or key files and invoke no external crypto command. Development and CI
dependencies include this library, so signing tests run rather than skip on macOS.
New snapshot format 6 verifies SHA-256 of RAM and device state on restore, both in the
CLI and in OpenVMM's opened-artifact admission. Versions 2–5 retain their previous
structural compatibility contract; snapshot verify labels that limitation. The
containment control deliberately downgrades an isolated test snapshot to version 5 and
shows that its same-length RAM change is admitted. Snapshots are not portable across
architecture or hypervisor. Full RAM hashing adds restore work. Warm-pool measurements
state when admission occurs; this pool admits and hashes clones before marking them ready,
and separately measures requests against those ready clones.
| Command / option | Behavior |
|---|---|
| `--workspace HOST:GUEST[:ro | rw]` |
--out NEW_DIR |
Publish successful private /out regular files after teardown; failed workloads publish no outputs. The destination must be new. |
--keep-alive |
Retain the managed VM after the first command; use its printed NVX-ID for later operations. |
exec ID -- COMMAND ARG... |
Execute with separate stdout/stderr and the guest's numeric exit status; --stream forwards frames immediately. |
cp LOCAL ID:/guest/path / cp ID:/guest/path LOCAL |
Transfer regular files byte-exact, with a 256 MiB bound and new destinations. |
logs ID --json [--follow] / ps / stop ID |
Ordered events, actual process/RSS/status, and idempotent confirmed teardown with a final receipt. |
bundle ID --output FILE / bundle verify FILE |
Reproducible evidence archive with checked inventory; excludes capabilities and VM RAM. |
--secret NAME |
Bind a host environment value to a disposable host proxy. API values never enter argv or guest environment. |
--egress-allow HOST:PORT with --secret |
Admit an exact TLS destination to credential injection; request Authorization/Cookie headers are stripped. |
--secret-header NAME:HEADER / --proxy-log FILE / --proxy-ca FILE |
Override injection header, retain redacted proxy decisions, or add a trusted upstream CA. |
--env NAME=VALUE |
Ordinary guest environment data; values are visible in guest RAM and snapshots. |
Mac exports pin their root directory and map the admitted workload identity to the host export owner. They do not impersonate Linux process credentials. Special files and host symlink escapes are denied. File outputs are bounded to 256 MiB and 10,000 entries. Credential bindings are excluded from warm snapshots; cloning a credential-bound template requires a fresh host binding and is currently rejected rather than reusing stale state.
| Command / option | Behavior |
|---|---|
warm --image REF [--backend BACKEND] --output DIR |
Capture a version-6 snapshot and frozen private scratch, bound to image, policy, artifact versions, architecture and backend. The default backend is HVF on macOS, KVM on Linux, or WHP on Windows. |
--runtime auto|python|dispatcher |
Python images retain a single-threaded initialized Python process behind a command barrier; other images retain the managed dispatcher. |
--profile ci --egress-allow IP:PORT on warm |
Bake an explicit default-deny network policy into the template. |
pool start --template DIR --size N |
Admit and restore N private clones before reporting readiness. Quota admission precedes VM creation. |
pool start --image REF --backend BACKEND --size N |
Prepare a template and then populate the pool. |
run --pool ID -- COMMAND ARG... |
Lease one ready clone, execute, retire it, write its receipt, and refill with a fresh clone. |
pool status ID / pool stop ID |
Inspect ready/leased/retired accounting, or stop all owned clones and wait for refill cleanup. |
benchmark --suite warm-pool --backend BACKEND --template DIR --runs 20 --output FILE |
Measure client request to first workload stdout, full completion, and the same workload's cold path. |
performance collect-warm --platform NAME --commit SHA --input FILE --output-dir DIR |
Validate sample medians and collect the three metrics as ordinary gated CSV results. |
Each restored clone repairs its trusted generation ID, machine ID, hostname, host clock, and CRNG before admitting work. Python workload children close the private control FDs; the parent is non-dumpable. A pool lease is never returned for reuse. RAM/state hashing and clone initialization occur before a clone enters the ready pool. The benchmark explicitly excludes pool preparation and refill from request timing.
test-microvm --scenario showcase-simulants runs ransomware, identity, fork spawning,
and exfiltration under the default policy, then requires each risky control to fail containment.
mcp serve [--backend BACKEND] selects the host backend and uses stdio by default. --transport http binds only
127.0.0.1, prints its URL and a private capability-file path, and requires bearer
authentication. It implements MCP 2025-06-18, including initialization, tool discovery,
streaming progress and notifications/cancelled. HTTP disconnect alone does not cancel work.
The six tools are nvx_run, nvx_exec, nvx_status, nvx_snapshot, nvx_files, and
nvx_secrets. The server owns only instances it created. File transfers stay beneath its
private files directory; secret discovery returns names and availability only.
--secret-name NAME explicitly admits a host binding. Duplicate idempotence handles replay
the same result; reuse with different arguments fails. Default limits are four concurrent
sandboxes, one vCPU per VM/four aggregate, 1024 MiB per VM/2048 MiB aggregate, 60 s wall,
and 4096 MiB retained snapshot storage. CLI quota options can lower or raise these bounds.
Python and TypeScript clients are in sdk/; installed packages include both clients and
compiled TypeScript output. Python and Swift consume the committed MCP contract vectors.
package --platform darwin-arm64|linux-arm64 --binary-only --destination DIR stages a
self-contained CLI, both SDKs, core executable, architecture-specific guest artifacts,
licenses, provenance and SHA256SUMS. Strict packages require a clean pinned core build.
macOS additionally requires Developer ID signing, its hypervisor entitlement and a
successful notarization assessment. --development labels a local preview explicitly.
Use --include-source when matching corresponding sources have been collected.
archive-release --source DIR --destination FILE.tar.gz snapshots the accepted inventory.
install --archive FILE.tar.gz --destination NEW_DIR verifies and installs the whole
runtime without overwriting another installation. Its entry point is NEW_DIR/bin/nvx.
download selects the host's ARM asset and retains the complete runtime alongside installed
artifacts. A published release and signing credentials are required for the remote
download → doctor → run release gate; a local archive smoke is reported separately.