Skip to content

Reference

Programmatic use

Drive mvmctl from scripts, CI jobs, and local automation.

Use mvmctl as the stable automation boundary when a language SDK does not yet expose the lifecycle operation you need.

For a comparison of local control entry points, see Control surfaces.

Terminal window
mvmctl machine ls --json
mvmctl machine build ./my-app --json
mvmctl manifest info ./my-app --json
mvmctl machine boot-report devbox --json
mvmctl run --dry-run --json -- python task.py

JSON output is intended for scripts. Human output can change for readability.

Treat non-zero exit as failure. Some commands use conventional sysexits values where the CLI can distinguish data errors, temporary failures, and timeouts; scripts should still branch on the command-specific JSON or stderr message when they need detail.

For guest process execution, preserve the guest exit code separately from the host transport result when the command provides it.

Use per-run state directories in CI so local developer state is not touched:

Terminal window
export MVM_HOME="$PWD/.mvm-test"
export CARGO_TARGET_DIR="$PWD/.mvm-test/target"
export CARGO_HOME="$PWD/.mvm-test/cargo"

The repository’s scripts/dev-env.sh, bin/dev, and just dev-* wrappers apply this pattern for worktree development.

Do not pass secrets through argv. Prefer the local secret store:

Terminal window
printf '%s' "$TOKEN" | mvmctl secret put api-token --value -
mvmctl secret get api-token

secret get checks presence and does not print the value.

Use argv arrays, not shell string interpolation:

import subprocess
result = subprocess.run(
["mvmctl", "manifest", "info", "./my-app", "--json"],
check=True,
text=True,
capture_output=True,
)

If you must invoke a shell, keep user input out of the shell string.