Skip to content

Policy profiles

Policy profiles are the first security decision for a sandbox run. Pick the least permissive profile that lets the workload do its job, then add filesystem, environment, network, and seccomp permissions deliberately.

For generated code, third-party code, model tool calls, and CI jobs, start with restrictive and relax only the specific boundary that blocks the workload.

mvmctl run supports four profile intents:

ProfileDefault useHost sharesEnvironment injection
restrictiveGenerated or untrusted code.Not allowed.Not allowed.
standardNormal local one-shot runs.Read-only only.Explicit --env KEY=VAL allowed.
devLocal iteration against a project tree.Read-only or writable.Explicit --env KEY=VAL allowed.
permissiveLast-resort local debugging.Broadest local mode.Explicit --env KEY=VAL allowed.

The default is standard. Use restrictive when the workload does not need host files or host-provided environment values:

Terminal window
mvmctl run --profile restrictive -- python task.py

Use standard when the workload needs explicit environment values or read-only input files:

Terminal window
mvmctl run --profile standard --add-dir ./fixtures:/fixtures:ro -- python task.py

Use dev for writable project-tree iteration:

Terminal window
mvmctl run --profile dev --add-dir .:/work:rw -- cargo test

Use permissive only when a local experiment needs the escape hatch. It requires an explicit acknowledgement so broad execution is visible:

Terminal window
MVM_ACK_PERMISSIVE_RUN=1 mvmctl run --profile permissive -- ./debug-script.sh

Host directory mounts are declared with:

Terminal window
mvmctl run --add-dir HOST:GUEST[:MODE] -- command

Rules:

  • MODE defaults to ro.
  • rw is allowed only with --profile dev or --profile permissive.
  • restrictive rejects --add-dir.
  • standard rejects writable host shares.

Prefer read-only shares for test inputs, source snapshots, fixtures, and model context. Use writable shares only for local developer workflows where syncing changes back to the host is the point of the run.

Use explicit environment injection:

Terminal window
mvmctl run --env TASK_MODE=check -- python task.py

Rules:

  • restrictive rejects --env.
  • standard, dev, and permissive allow explicit --env KEY=VAL.
  • Do not pass secrets through argv or --env; use managed secret references where a secret is required.

Environment values are easy to leak through process listings, shell history, debug output, and crash reports. Treat them as configuration, not as a secret delivery path.

Use dry-run mode to check a run plan without resolving an image, booting a VM, executing the command, or writing a receipt:

Terminal window
mvmctl run --dry-run --json --profile restrictive -- python task.py

Dry-run output is redacted. It is useful in CI because policy failures can be caught before a workload starts.

Named VM launches expose a separate seccomp tier:

Terminal window
mvmctl up ./my-app --name agent-sandbox --seccomp standard

Supported tiers are:

TierUse it when
essentialThe workload needs the smallest syscall surface the current guest can boot with.
minimalThe workload is simple and should run with a reduced syscall set.
standardDefault named-VM posture.
networkThe workload needs network-oriented syscalls beyond the standard profile.
unrestrictedLocal debugging only; avoid for untrusted code.

The selected tier is recorded in the signed admission profile for audit.

WorkloadStart withAdd only if needed
Model-generated codemvmctl run --profile restrictiveRead-only fixtures after review.
Code interpretermvmctl run --profile restrictiveA bounded work directory or receipt output.
CI validationmvmctl run --profile standardRead-only source share and explicit non-secret env.
Local developmentmvmctl run --profile devWritable project share.
Long-running local servicemvmctl up --seccomp standardExplicit ports, volumes, and readiness checks.

Security-first defaults should feel slightly strict. A denied --env or writable share is a useful signal that the run is crossing a boundary.

Profiles affect the run’s policy surface, so include evidence with automated runs:

Terminal window
mvmctl run --profile restrictive --receipt /tmp/run-receipt.json -- python task.py
mvmctl receipt verify /tmp/run-receipt.json

Use audit and receipts for portable proof and host-local investigation. Keep policy identifiers, run IDs, and audit IDs in higher-level logs instead of copying raw command payloads there.