pvisor command reference
pvisor is the product command for a single Run and for durable
environments.
Full command examples for Host, OCI VM, and transparent host-rootfs VM
are in
Run workloads with pVisor.
Find the command you need
Use the smallest surface that matches your next decision:
- Run an Agent: start with
pvisor run, then usereview,inspect, andapplyto decide what reaches the project. - Understand a boundary: use
statusandinspect, then read the execution guide before changing providers. - Keep a workspace: use
env createandenv execfor a reusable staged environment; useenv applyorenv dropto close the loop. - Continue a trajectory: use
replayonly when you already have a supported trajectory and want a fresh sandbox; begin with the replay guide.
If this is your first command, do not start with the full option list below:
pvisor run --stage ./runs/task-001 -- codex
pvisor review last
pvisor apply last --path src
The reference that follows is organized by lifecycle. Options that affect the same Run are intentionally described together so that a copied command has a clear verification step.
pvisor
├── run execute one Agent Run
├── replay replay and continue an Agent-native trajectory
├── env manage durable reusable environments
├── status aggregate Run, filesystem, and network state
├── inspect open a read-only Run view
├── review review the durable Run Bundle
├── checkpoint snapshot a stopped transactional upper
├── fork start a child Run from a logical checkpoint
├── apply commit a stopped Run's filesystem stage
└── drop discard a stopped Run's filesystem stage
Safe first run
pvisor -- codex
pvisor review last
Host execution uses safe-best-effort isolation by default. --stage <PATH> opts
into an OverlayFS stage for the current workspace, creates an independent Run
and writable stage at the supplied path (or in the generated Run record
directory when no explicit stage is supplied),
retains changes for manual review, and writes run-bundle.json with mode 0600.
On Linux, the default host executor self-executes through pVisor's rootless
launcher before the async runtime reaches the Agent. User/mount/PID namespaces,
an in-namespace PID 1 descendant reaper,
minimal bind-projected root plus chroot, a kernel-negotiated Landlock ABI v1-v3 policy, closed
inherited descriptors, no_new_privs, and an empty capability set make
workspace containment non-bypassable for the Agent process tree.
--overlaynet-deny-all adds a private network namespace; the
public/allowlist proxy modes remain cooperative. On macOS the default safe
host executor installs a generated Seatbelt policy that makes staged writes
non-bypassable. For deny-all Runs it blocks IP and ambient host Unix sockets,
while retaining the exact AgentCtl and Run-local IPC. Reads and selective
network policy remain ambient/cooperative and are labeled separately in the
Bundle. Docker and KVM transports retain the same outer Run, OverlayFS,
AgentCtl state observation, and pChronicle control plane.
After completion:
pvisor review last
pvisor checkpoint last --name before-experiment
pvisor fork last --checkpoint before-experiment -- codex
pvisor apply last --all # or: pvisor drop last
The CLI checkpoint is stopped-consistent. Embedded hosts can call
RunHandle::checkpoint: pVisor publishes an AgentCtl quiesce directive,
requires every Session frozen into the checkpoint to report the matching
quiesced state, snapshots the raw upper, then publishes continue. Logical
checkpoints preserve filesystem and cooperative client safe-point boundaries,
not process memory.
A durable environment has a stable name and a reusable OverlayFS upper:
pvisor env create dev --target ./project
pvisor env exec dev -- make test
pvisor env shell dev
pvisor env inspect dev -- git status --short
pvisor env stop dev
pvisor env start dev
pvisor env apply dev --path src # commit the selection; the rest stays staged
pvisor env apply dev --all # commit remaining changes and reset to an empty stage
pvisor env drop dev # discard changes and reset to an empty stage
pvisor env delete dev --force
Default metadata lives in ~/.persisting/envs and can be overridden
with --root or PERSISTING_ENV_HOME. start / stop control whether
new sessions are accepted; they do not mean a resident VM. Each
exec / shell mounts the same writable upper, so changes persist
across commands. inspect uses a kernel-enforced read-only view.
apply --all or drop do not flip a terminal Overlay back to staged
in place; they create a monotonically increasing Overlay generation.
After a command takes the environment lease it re-reads the generation
so metadata from before the reset cannot overwrite the new stage.
Replay an Agent trajectory
pvisor replay assumes the caller has normally created a fresh sandbox. It
replays complete tool batches through after_step, rebuilds the selected
Agent native context with fresh observations, and then starts the live Agent:
pvisor replay \
--agent claude-code \
--trajectory /input/session.jsonl \
--after-step 30 \
--agent-entrypoint /usr/bin/claude \
--boundary-user-prompt 'Review the fresh observation before continuing.'
OpenHands, mini-swe-agent, Pi agent, OpenCode, Codex, and SWE-agent use the model endpoint and
credentials already present in their environment. Pi requires its exact
0.83.0 runtime and accepts native RPC event JSONL containing the core
read, bash, edit, and write tools. Claude Code uses a temporary bridge owned
by SandboxReplay because its native resume transport inserts wake-up messages.
The bridge validates and removes that exact Resume Transport envelope before
forwarding the model request. It does not enable pVisor Gateway, capture model
traffic, or persist a bridge audit.
OpenCode requires the exact 1.17.7 runtime and its native
opencode run --format=json event JSONL. Codex requires the exact 0.149.0
runtime and Codex rollout response_item JSONL. Both rebuild the native prefix
in the fresh sandbox and invoke their native resume command for continuation.
Codex derives its native session ID from the trajectory's session_meta; the
request session_id is only a model-router/Run key and cannot override it.
Continuation fails closed when the native session is missing.
The equivalent strict replay TOML is:
[replay]
agent = "claude-code"
trajectory = "/input/session.jsonl"
after_step = 30
agent_entrypoint = "/usr/bin/claude"
max_steps = 200
session_id = "task-291-attempt-1"
replay_only = false
disable_thinking = true
boundary_user_prompt = "Review the fresh observation before continuing."
Pi uses the same CLI/TOML surface. When the runtime is installed at
/opt/pi-agent, for example:
pvisor replay --agent pi-agent \
--trajectory /input/pi-agent.events.jsonl \
--after-step 30 \
--agent-entrypoint /opt/pi-agent/bin/pi
Replay has three modes. The default replays the prefix and continues;
--replay-only executes the prefix and stops before a model request; and
--prepare-only constructs the prefix without executing tools or requiring a
runtime. --max-steps is the total action budget, including replayed actions.
--allow-stale-observations is an explicit Claude-only escape hatch that marks
the v3 result degraded.
--boundary-user-prompt TEXT appends one user message after the final fresh
observation and before the first live model inference. The TOML spelling is
replay.boundary_user_prompt. It is ignored for inference in prepare-only and
replay-only modes, and an omitted option preserves the unmodified replay
boundary. Structured results and replay journals store only injection state,
length, and a digest; Agent-native prepared or continued trajectories may
contain the user message.
The result schema is sandbox-playback.result/v3, with typed phase, quality,
and agent_status fields plus state/output locations, artifacts, and an optional
structured failure. Existing non-Claude callers that used replay_only = true
only to construct a prefix must migrate to prepare_only = true.
disable_thinking belongs to [replay] and is also exposed as
--disable-thinking. Claude Code's protocol bridge applies it to the upstream
request; OpenCode omits its --thinking flag when it is set. It does not turn
on Gateway capture. Optional [run], [overlayfs], and [overlaynet] sections
create an outer managed pvisor run; they do not change the inner replay model
path.
By default, replay's internal state, WAL, manifest, fresh-observation
comparisons, and native working files remain under
/tmp/pvisor-sandbox-replay and disappear with the sandbox. Replay does not
enable pVisor Gateway, pChronicle, a model-traffic capture store, or a Claude
Resume Transport audit. A caller that explicitly selects --state-dir or
--output-dir owns those files. Use --replay-only to execute the prefix and
stop before live inference, or --prepare-only to construct it without execution.
One configuration model
pvisor run has one canonical RunConfig. TOML and command-line options are
two representations of the same fields. --spec is optional and explicit; a
file beginning with a JSON object is treated as a prepared RunSpec, otherwise
it is read as TOML RunConfig. pVisor does not discover a hidden project file.
pvisor run \
--name codex \
--overlayfs-path /workspace \
--overlayfs-compose /path/to/project \
--overlayfs-backend directory \
--overlayfs-commit manual \
--overlaynet-allow api.openai.com:443 \
--overlaynet-deny 169.254.0.0/16 \
--overlaynet-limit 10mbps \
--gateway-mode capture \
--gateway-level dialogue \
--gateway-route \
'name="openai", provider="openai", upstream="https://api.openai.com/v1", api_key_env="OPENAI_API_KEY"' \
--record-format lance \
--record-destination ./warehouse \
-- codex
--record-format lance starts pchronicle serve --control 127.0.0.1:0 DATASET;
pVisor sends shared EventRecord values and waits for durable
acknowledgements. Use --record-format json for local JSONL or a JSON warehouse
archive.
All newly persisted records contain both timestamp (RFC3339 UTC) and
timestamp_unix_ms (Unix milliseconds). They describe the same observation
time and must agree within one millisecond. Record ordering remains defined by
source + seq; timestamps are correlation metadata rather than the ordering
source of truth.
The equivalent TOML is:
[run]
agent = "codex"
executor = "container"
command = ["codex"]
[container]
runtime = "docker"
image = "example/codex-agent:latest"
network = "host"
[overlayfs]
path = "/workspace"
compose = ["/path/to/project"]
backend = "directory"
commit = "manual"
[overlaynet]
mode = "proxy"
policy = "allowlist"
[[overlaynet.rules]]
host = "api.openai.com"
ports = [443]
[[overlaynet.deny]]
host = "169.254.0.0/16"
[[overlaynet.limits]]
bytes_per_second = 1250000
[gateway]
mode = "capture"
level = "dialogue"
[[gateway.routes]]
name = "openai"
provider = "openai"
upstream = "https://api.openai.com/v1"
api_key_env = "OPENAI_API_KEY"
[chronicle]
mode = "lance"
Run it with pvisor run --spec run.toml. Explicit CLI scalars replace TOML
scalars. Supplying any repeated CLI field (--overlayfs-compose,
--overlaynet-allow, --overlaynet-deny, --overlaynet-limit, or
--gateway-route) replaces that complete TOML list.
The command after -- replaces run.command.
--container-image IMAGE selects the OCI container executor automatically;
--executor container makes the choice explicit. The transport resolves a
matching static linux-amd64/linux-arm64 pVisor, mounts it into the image,
overrides the entrypoint, and invokes the normal
pvisor run --executor host --spec ... path. The Agent command is carried
inside the RunSpec rather than exposed in OCI runner argv. The injected
pVisor creates its own AgentCtl and returns a typed RunResult. The final
OverlayFS cwd and session Gateway configuration are mounted at stable paths.
User mounts are repeatable TOML inline tables, for example:
pvisor run \
--container-image example/codex-agent:latest \
--container-pvisor-binary ./dist/pvisor-linux-amd64 \
--container-platform linux/amd64 \
--container-network none \
--container-mount \
'source="/host/cache", target="/cache", read_only=false' \
-- codex
The in-process Gateway and explicit OverlayNet proxy currently require
container.network = "host", because their injected addresses are host
loopback endpoints. Bridge and no-network modes are valid when these drivers
are off. The executor records container isolation but does not claim full
capability enforcement.
--executor vm uses statically linked libkrun and its embedded init to boot a
minimal Linux guest. --rootfs image=IMAGE selects this executor and pulls an
OCI/Docker image directly, without invoking Docker, Podman, or Buildah. When no
explicit rootfs is supplied, the default is ubuntu:latest. Manifests
and layer digests are verified, the host architecture selects linux/arm64 or
linux/amd64, and the unpacked rootfs becomes the immutable lower layer of a
pVisor OverlayFS. --image-store overrides the platform cache directory.
OCI cache targets are marked immutable, and this protection survives logical
checkpoint/fork, so pvisor apply cannot mutate a rootfs shared by other Runs.
On Linux, --rootfs host selects the host / as the VM rootfs lower and
selects the VM executor when --executor is omitted. --rootfs <PATH> selects
a prepared directory and --rootfs image=<PATH> selects an OCI image or image
path. These forms are mutually exclusive, and host rootfs is rejected on macOS.
The OverlayFS view is selected with --overlayfs-path, the absolute path the
Agent sees. Repeat --overlayfs-compose to layer host directories in
bottom-to-top order; the current workspace remains the implicit bottom layer.
When --overlayfs-path is omitted, the current workspace is used as the view
source and pVisor places the merged mount in a managed per-Run path to avoid
mounting over its own lower directory.
With a guest workspace path, writes outside that workspace use a temporary
root upper and are discarded when the VM exits; workspace changes use the
durable OverlayFS stage.
The merged rootfs is guest /, and /workspace becomes the guest cwd. On both
Linux and macOS, a vendored libkrun serves pVisor's rootfs and workspace
copy-on-write unions directly over virtio-fs. The VMM never re-exports a host
FUSE mount and does not materialize or reconcile either tree. Linux uses
KVM and Apple Silicon macOS uses HVF through the same executor. libkrunfw is
installed beside pVisor in wheels. Source builds otherwise download the pinned
official release into a SHA-256-verified platform cache; on macOS /usr/bin/cc
turns its prebuilt kernel bundle into the required dylib. A system directory can
still be selected with --vm-library-dir. OverlayNet auto uses the
non-bypassable VM smoltcp IPv4 TCP/DNS driver, while Gateway capture uses an
internal route through the guest virtual router. Linux additionally confines
the VMM with namespaces and Landlock. The macOS VMM still has the invoking
user's host permissions, so the first OCI-image version must not be treated as
a hostile multi-tenant boundary despite the guest-kernel isolation.
On host/container execution, the four visible OverlayNet policy flags and
Gateway capture automatically enable the proxy driver. Any --overlayfs-path,
--overlayfs-compose,
--stage, --overlayfs-backend, or --overlayfs-commit option
automatically enables OverlayFS; no separate mode switch exists. The workspace
is the implicit base; explicit compose layers are applied in the order given.
An explicit --stage is the unified record
directory for metadata, trajectory, and filesystem state. When a stage is nested inside a base or compose layer, pVisor hides
that subtree from the merged view and rejects guest attempts to recreate it.
libkrun Runs create no live host mountpoint, preventing host indexers from
recursively entering <stage>/merged. The reverse topology, where a
stage contains a lower layer, is rejected. Both
commit=apply and the later pvisor apply command are rejected
for composed Runs until pVisor can materialize a complete merged-vs-base diff
safely.
On host/container execution, OverlayNet policy applies to traffic routed
through the explicit proxy and does not claim non-bypassable host network
isolation. On a libkrun VM, auto attaches non-bypassable smoltcp IPv4
TCP/DNS; off leaves the guest offline. --overlaynet-deny-all supplies the
same default-deny policy to the active driver. Host/container direct sockets
remain ambient, while a VM Gateway route remains available through the guest's
virtual router for configured model traffic.
Run project discovery
The current directory is the default project association. When OverlayFS is
enabled, --overlayfs-compose identifies reusable host layers and
--overlayfs-path identifies the Agent-visible view. Each Run receives an
independent directory under pVisor's default records root. If that root would be inside
the selected OverlayFS base or a compose layer, pVisor instead uses the system
temporary Run root to keep the writable stage disjoint:
project/ # reusable workspace / default base
~/.persisting/runs/
└── run-<uuid>/ # one generated Run and default stage
├── run.json
├── run-bundle.json # mode 0600; outcome + safety + changes + effects
├── overlay.json # when OverlayFS is enabled
├── upper/ # or a Run-named Jujutsu workspace upper
├── merged/
├── checkpoints/
├── lease.lock
├── control.sock # while a live OverlayFS Run is available
├── .capture/ # when OverlayNet/Gateway is enabled
└── chronicle/ # default pChronicle location
Lifecycle commands accept a Run id, Run directory, project workspace,
run.json, upper, or merged path. A project workspace selects its latest Run:
pvisor status /path/to/project
pvisor inspect /path/to/project -- rg TODO .
pvisor apply /path/to/project --all
pvisor apply /path/to/project --path src --path tests/unit
pvisor apply /path/to/project --include 'docs/**' --exclude 'docs/generated/**'
pvisor apply /path/to/project --target /path/to/another-target --all
pvisor drop /path/to/project
inspect creates a separate kernel-read-only view. apply and drop refuse
to mutate a live Run. A filtered apply is dependency-closed and repeatable:
unselected paths remain staged, while opaque directories and hard-link groups
remain atomic. Each successful batch is persisted in apply-ledger.json.
The overlay records a durable first-touch fingerprint for every mutated target
path. apply fails closed if a selected target path changed after staging;
prepared batches recover forward, and individual non-directory replacements
commit with a same-directory atomic rename. The host filesystem still provides
no single atomic commit point for an arbitrary multi-file batch.
Applying all remaining changes or dropping the stage is terminal; drop cannot
undo already applied batches, and apply cannot recover discarded changes.
Terminal cleanup removes upper, work, and other disposable staging data but
retains compact Run/Overlay metadata, the apply ledger, and pChronicle history.
Related workflows
- Run your first Agent for the shortest complete loop.
- Execution environments for choosing a provider.
- Review and apply changes for filtered, repeatable apply.
- Network control and capture for other Effect dimensions.