Skip to content

Programmatic use

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 ls --json
mvmctl build ./my-app --json
mvmctl manifest info ./my-app --json
mvmctl 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_DATA_DIR="$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.