Local mvm validation
CI exercises the host CLI and SDK contracts against a fake-mvmctl shim. To prove the generated flake actually evaluates against the real mvm/guest-lib and (eventually) boots a VM, you run two recipes locally.
Both recipes need a local mvm checkout. Set its path via MVMFORGE_MVM_REPO:
export MVMFORGE_MVM_REPO=/path/to/mvmmvmctl itself doesn’t need to be on PATH — the recipes invoke it via cargo run --manifest-path "$MVMFORGE_MVM_REPO/Cargo.toml".
just real-mvm-check
Compile an SDK entry, then run nix flake check against the generated artifact. No VM boot. Lighter than real-mvm-up; runs on macOS without a Lima dev VM. Requires native nix on PATH.
just real-mvm-check # default: tests/corpus/hello-app/app.pyjust real-mvm-check tests/corpus/with-source/app.pyWhat this validates:
- The Python SDK loads and emits canonical IR.
- The host parses and validates the IR.
- The compile step writes deterministic
flake.nix,launch.json, andsrc/. - Real Nix evaluates the flake, resolves all inputs (the pinned
mvmflake,nixpkgsviafollows), and reportsmvmforge.workload’s shape. - The
mkGuestderivation is actually constructible.
What it does not validate:
- Actual VM boot. The flake-check stops short of
nix build; for a bootable rootfs, seereal-mvm-up.
just real-mvm-up
The full pipeline: mvmforge up <entry> calls mvmctl up --flake <artifact>. mvmctl evaluates and builds the flake, then boots the resulting VM via your platform’s backend. Requires:
- Linux/KVM, or
- macOS 26+ on Apple Silicon (Apple Virtualization), or
- macOS < 26 with a Lima dev VM (
mvmctl bootstrapfirst).
just real-mvm-up tests/corpus/with-source/app.pyForward extra mvmctl up flags via MVMFORGE_UP_ARGS:
MVMFORGE_UP_ARGS='--hypervisor apple-container --name myvm' \ just real-mvm-up tests/corpus/with-source/app.pyOr pass them on the command line directly via mvmforge up’s -- separator:
mvmforge up tests/corpus/with-source/app.py -- --hypervisor apple-container --name myvmUseful flags from mvmctl up --help:
| Flag | Purpose |
|---|---|
--hypervisor | firecracker, apple-container, qemu, docker |
--name | VM name (auto-generated if omitted) |
--cpus | Override vCPU count |
--memory | Override memory (e.g., 512M, 4G) |
-p HOST:GUEST | Port mapping |
-e KEY=VAL | Inject env var |
--detach / -d | Background mode |
--watch | Auto-rebuild + reboot on flake changes |
What success looks like
A successful boot of tests/corpus/with-source/app.py:
- Compile lands
flake.nix+launch.json+src/hello/{__init__,__main__}.py. mvmctlevaluates the flake;nix buildproducesmvm-with-sourcecontainingvmlinux,rootfs.ext4, andimage.tar.gz.- The selected backend boots the VM.
mkGuest’s busybox init starts thewith-sourceservice.services.<id>.preStartsymlinks the bundled source to/app.services.<id>.commandrunscd /app && exec python -m hello.hello from a bundled mvmforge workloadappears in the boot log.
Capture the log to file when validating:
MVMFORGE_UP_ARGS='--hypervisor apple-container' \ just real-mvm-up tests/corpus/with-source/app.py 2>&1 | tee /tmp/mvm-boot.logThe first successful boot’s log gets committed to tests/fixtures/real-mvm-boot-log.md per the runbook in that file.
Working against a local mvm without a real boot
If you just want to iterate on the generated flake without going through mvmctl:
export MVMFORGE_MVM_FLAKE_URL="path:$MVMFORGE_MVM_REPO/nix/guest-lib"mvmforge compile manifest.json --out /tmp/artifactnix flake check /tmp/artifactnix eval path:/tmp/artifact#packages.x86_64-linux.default.nameNote: byte-reproducibility holds per-machine but not across machines with different MVMFORGE_MVM_FLAKE_URL overrides (per ADR-0007 §6).