pvisor command reference¶
pvisor run creates a container in its own rootless Linux VM. pvisor spawn
creates a Job; omitting the subcommand also selects spawn. Job lifecycle and
change-review commands operate directly on Jobs without a job subcommand, and
replay creates a Job from a trajectory. See the
execution model for how Job, Run and Attempt
relate; existing Job IDs and on-disk records keep their run-*
and Run Bundle names.
Full command examples for Host, OCI VM and transparent host-rootfs VM
are in
Run workloads with pVisor.
Root help (pvisor or pvisor --help) groups commands under Commands
(Docker-compatible container commands), Agent Commands (spawn, status,
kill, review, apply, drop, inspect, checkpoint, suspend, resume, fork) and Tools
(installed replay/tui companions, cache, feature, help). It shows a short
run-review-apply example; use pvisor help COMMAND for detailed options.
Help, version and feature queries emit no startup logs. Execution commands emit
process.entry and cli.parsed only after command parsing. These markers do
not measure the complete process-entry or argument-parsing overhead.
Container commands¶
run, build, pull, images, rmi, exec, ps, start, stop, rm, logs and network
use a Docker-style option subset with one VM per container and no central daemon.
Use pvisor COMMAND --help for supported options. Job options such
as --safe, --stage and --executor belong to spawn, not container run.
pvisor pull alpine:3.20
pvisor run --rm alpine:3.20 echo hello
pvisor run -d --name redis redis:7
pvisor exec redis redis-cli ping
pvisor stop redis
pvisor rm redis
pull --platform linux/amd64 or linux/arm64 selects the image architecture;
linux/arm64/v8 is an alias. Omission uses the native platform. Foreign-image
downloads are supported, but run and start require native images because CPU
emulation is not implemented. This profile does not implement Docker Engine API,
Compose or credential helpers. Containers use their own image store;
Job review/apply/drop and checkpoints continue to address Jobs.
Same-host container networks¶
network create/ls/inspect/rm manages logical networks in the current PVISOR_DOCKER_HOME. The default bridge supports IP-based communication; user-defined networks also provide container-name DNS. --network none omits the NIC, and --ip requires a user-defined network.
pvisor network create --subnet 172.20.0.0/24 dev
pvisor run -d --name web --network dev python:3.12-alpine python -m http.server 8000
pvisor run --rm --network dev alpine:3.20 wget -qO- http://web:8000/
pvisor network inspect dev
pvisor stop web
pvisor rm web
pvisor network rm dev
Members support IPv4 TCP, UDP, ICMP and targeted ARP; external egress retains the TCP/DNS profile. Peer traffic needs no -p; publish ports for host access. Only IPv4 /24 networks in the allowed ranges and one network per container are supported. IPv6, host networking and connect/disconnect are unsupported. Stopped containers retain addresses and attachments, so remove containers before removing their network.
Image builds¶
pvisor build -t app:local --network=none .
pvisor run --rm app:local
pvisor build -f Dockerfile --build-arg VERSION=1 \
--output type=oci,dest=app.oci.tar -t app:local .
build accepts local contexts, -f, repeated -t/--build-arg, --target,
--platform (native only), --pull, --no-cache, -q, --iidfile,
-o type=oci,dest=PATH, --network default/none, --cpus, -m/--memory,
--timeout (seconds per RUN, default 600) and --progress auto/plain.
Every RUN uses a fresh VM with a private rootfs overlay. Context files are
captured privately and copied only by COPY; they are never mounted into RUN.
Timeout/failure reaps the VMM; frontend death closes its lifetime pipe. Guest
processes do not survive a RUN boundary. Network defaults to the existing public
IPv4 TCP/DNS policy; none omits the NIC. CPU/RAM flags size the VM.
Supported Dockerfile instructions are FROM/AS, RUN, local COPY, completed-stage
COPY --from, ARG, ENV, WORKDIR, USER, CMD, ENTRYPOINT, SHELL, LABEL, EXPOSE,
VOLUME metadata and STOPSIGNAL. COPY supports JSON/globs, numeric --chown and
octal --chmod; .dockerignore and Dockerfile-specific ignores are applied.
Simple $NAME/${NAME} substitutions are supported. --build-arg NAME forwards
only that variable; arguments are not secret storage. Images use a single flat
OCI layer, preserving merged deletes, guest ownership/mode, symlinks and hardlinks.
A successful build publishes local tags usable by run; ordinary failures clean
private staging and retain prior tags. Abrupt death can leave unpublished staging.
OCI output requires a new file. There is no instruction cache; --no-cache is
accepted and every step runs.
This is a Docker build subset: remote/stdin contexts, ADD, ONBUILD, heredocs, RUN mounts, secrets/SSH/cache mounts, named COPY owners, external-image COPY, extended substitutions, foreign execution, device nodes and file capabilities are rejected. COPY sources cannot traverse symlinks. Push, Docker archive output and multi-platform builds are not implemented. Apple Silicon/HVF has native acceptance coverage; Linux/KVM needs separate runtime acceptance.
Job owners and durable operations¶
pvisor spawn starts a frontend and a private Job owner. Live status, kill, VM
controls and execution capture connect to the owner recorded in the Job;
ordinary commands need no socket arguments. Stopped status, review, apply, drop
and inspect run directly in the caller under job.lock. Apply/drop refuse a live
owner still holding stage. Interactive execution keeps the original terminal,
allows Ctrl-C to reach the workload, and restores foreground ownership at exit.
The instance protocol is pvisor-instance-control-1; compatible caller binaries
need not match the owner's digest. Same-UID private sockets use bounded newline
JSON and never accept terminal descriptors or Guest AgentCtl tokens. Lost replies,
timeouts and cancellation can follow effects and are never automatically retried.
Before upgrade, drain old Jobs and stop the old --pvisor-internal-host with the
old binary. New CLIs ignore old generation sockets and read records lacking live
control as disk state only. The daemon supervisor is unchanged. See
Host and Guest AgentCtl for ownership,
record validation and platform limitations.
Experimental features¶
Query runtime features in the caller without starting a Job or connecting to an owner:
pvisor feature
pvisor feature list --json
pvisor --feature workload-aware-memory-offloading feature --json
Each entry includes name, stage, default, enabled and description. default is the registered default; enabled reflects that default plus this query's --feature options. The query does not load a Run configuration or inspect a live Job. An enabled flag does not certify platform availability or runtime success.
Use the repeatable global --feature NAME option before or after spawn; comma-separated names are also accepted. Unknown names reject. Arguments after -- belong to the workload.
| Feature | Stage | Default | Scope |
|---|---|---|---|
workload-aware-memory-offloading |
experimental |
false |
EXP-001 M0 native VM vCPU observation; automatic offload is not implemented |
workload-aware-memory-offloading requires the final executor to be VM and a Linux x86_64/KVM or Apple Silicon macOS/HVF build. It does not replace an executor selection. HVF runtime validation remains outstanding. Observation is enabled through the native runner; no live Host observation query is exposed yet.
Feature enables apply to spawn and feature queries. Other lifecycle commands reject them; execution resume/fork retain the saved configuration. [features] in an explicit Run configuration can also enable features; CLI enables override false, omission preserves the configuration. There is no CLI disable option or persistent feature enable action. See feature configuration. Runtime features are separate from Cargo build features and do not change existing VM memory flags.
Daemon and cache commands¶
pvisor spawn and subsequent Job commands provide native execution and review; container commands such as pvisor run manage independent VMs. Invoke pvisor-daemon directly to prepare templates and serve partial OpenSandbox/E2B profiles, or enable cache/pool components independently. pvisor cache is the cache client.
pvisor-daemon prepare-image --help
pvisor-daemon serve --help
pvisor-daemon memory-pool --help
pvisor cache --help
Add --memory-pool to pvisor-daemon serve --sandbox to enable its default-off detached pool component. pvisor cache prepare/publish/list/stat/read are client commands; cache serving is an optional daemon component. Node runtime protocols serve native callers, without a daemon acquire/release adapter. NativeRuntime embeds VM execution; checkpoint/fork and stage/apply APIs are absent. See the daemon guide for ownership, installation and deployment boundaries.
Other unknown names follow default execution rules. The ctrl name
is an explicit exception: pvisor ctrl, pvisor ctrl --help and
pvisor help ctrl reject with migration guidance before Job admission, rather
than default-running a program named ctrl. Use the live VM commands below.
pvisor spawn -- ctrl still expresses explicit workload intent; it does not
invoke a control API. Use pvisor -- COMMAND for explicit default
execution.
Find the command you need¶
- Run a command: start with
pvisor spawn, then usestatus --review,inspectandapplyto decide which changes reach the project. - Understand the execution boundary: use
statusandinspect, then read the executor guides. - Continue a trajectory: use
replayonly when you already have a supported trajectory, after reading the replay guide.
To get started, run and review a Job:
last resolves only Jobs that belong to the current workspace in default
storage. With --stage PATH, pass that path or the Job ID to later commands;
see Jobs and storage.
Add --tui for a Zellij-style terminal frame, bottom status bar and floating
review panel:
Keyboard input goes to the shell or Agent by default, and the terminal always
keeps its full width. The header shows the working directory and the file and
network boundaries; the bottom bar shows run state, elapsed time and nonzero
access anomalies, and highlights pending authorization. Detailed counters stay
in the panels.
Press Ctrl-] to open the menu, which replaces the same line with shortcuts.
pVisor's own startup diagnostics appear in the
Log panel rather than the Agent terminal. Press Ctrl-] to enter command mode,
then r, f, n, u, l or p to open the Overview, Files, Network, Job,
Log or Permissions panel, and ? for key help. In a panel, switch views with
Tab or 1–6, scroll with j/k, and return to the Agent with Esc or
Ctrl-]. Press Ctrl-] twice to send the key to the Agent literally.
Staging and storage¶
The table below describes the default CLI behavior. Explicit commit settings, writable shares and application-compatibility policies can change where writes go.
| Mode or directory | Write destination | After exit |
|---|---|---|
| Ordinary host, no staging | Original workspace | Written to the host; drop cannot undo it |
--safe or --ask workspace |
Copy-on-write stage in Job storage | Retained for status --review, apply and drop |
Workspace with explicit --stage PATH |
The named stage | Retained; the option also enables staging |
| VM workspace | The named stage or default Job storage | Retained |
| Other writes to the VM root | Private temporary upper | Discarded when the VM exits |
--safe HOME / CODEX_HOME state |
Separate private stage | Discarded at exit; not part of the workspace Run Bundle |
Explicit --mount SOURCE:write |
Host SOURCE | Written directly and persistently; bypasses workspace apply/drop |
Job records and the Run Bundle are stored in run storage. --stage PATH chooses
the location; it is not a prerequisite for --safe/--ask to retain workspace
changes. File review does not undo remote side effects that already happened.
Filesystem parameters¶
A macFUSE temporary workspace on macOS uses the directory where pVisor was
launched as its default lower; /Volumes/pvisor-* is the merged-view mount
point and contains the files already present in that directory. An explicit
working directory or OverlayFS base takes precedence.
An ordinary host Job writes workspace changes through to the lower by default.
--safe and --ask retain workspace changes in Job storage by default, to be
handled later with status --review, apply or drop. --stage PATH only
chooses the storage location and is not a prerequisite for retaining
changes.
pvisor spawn --stage ../run-stage -- codex
pvisor spawn --safe --mount /opt/zcode:read --mount /var/lib/zcode:write -- zcode
pvisor spawn --access '**/.ssh:deny' -- zcode
pvisor spawn --access '.env:ask' -- codex
--mount SOURCE:read grants a host executor read-only access at the original
absolute path and requires --safe or --ask; SOURCE:write modifies host
storage directly. Neither rewrites TARGET, and neither may overlap the
workspace, Job storage or writable runtime paths; a read-only share cannot live
under Linux's private /tmp. Explicit shares bypass the workspace OverlayFS ask
rules.
--mount SOURCE[:TARGET]:stage instead adds SOURCE as a lower layer of the
workspace copy-on-write view; it is not an independent directory mount.
--access PATH-GLOB:deny|ask|warn accumulates configuration, preset and CLI
rules by default: deny refuses, ask prompts, and warn allows with a warning.
Clearing configuration and default file protection requires an explicit
--clear-access before adding CLI rules. deny > ask > warn.
The former :read warning spelling is rejected; use :warn, or
--mount PATH:read for true read-only access.
Use pvisor --ask -- bash to ask before matching an ask file rule or reaching
an unlisted proxy network destination; --ask also enables --tui and
--safe. Specifying --access ...:ask automatically enables the audit TUI and
the safe staging view, with no separate --ask or --tui.
The prompt uses Tab or the up/down arrows to switch scope, retention and
buttons, left/right to choose, and Enter to confirm on a button; the deny button
is selected by default. Press s, w or u to pick the session, workspace or
user scope. Press 1 to allow only this file, 2 to allow the containing
directory, or 3 to allow the same suffix; press d to deny this target. For
an unlisted proxy network destination, the --ask prompt can press 1 to allow
only this target or 2 to allow the current domain and its subdomains; both
choices are limited to the current port and transport, and an IP address cannot
use domain scope. An explicit deny rule still refuses directly without opening
the prompt.
Session decisions are written to audit-policy.json in the current Job
directory, and workspace and user decisions to the permissions section of
~/.config/pvisor/config.toml (or the pvisor/config.toml under an absolute
XDG_CONFIG_HOME). A decision is applied automatically when the same scope
matches again, and every decision is recorded in audit.jsonl. The workspace is
identified by its canonical working directory; a new TUI Job loads the rules
with session > workspace > user precedence, and within a layer the last matching
rule wins. Persisted file rules use original absolute paths, so a user-level
suffix rule can cover other workspaces; saving preserves other configuration
settings and comments. Saved decisions are used only for ask-matched access and
cannot override a static deny or an outer sandbox.
In the Permissions panel, select a decision with j/k and press x twice to
remove it; the access then falls back to a broader rule or a new prompt. Removal
does not close already-open file handles, and other running TUIs pick up the
change on their next launch. These records are retained after the Job by
default; --stage PATH chooses the location.
Proxy network auditing is a cooperative boundary: direct connections that bypass the proxy do not trigger this prompt.
Commands:
run Create and run a container in its own VM
pull Download an OCI image
images List local images
rmi Remove image references
exec Execute a command in a container VM
ps List containers
start Start stopped containers
stop Stop containers
rm Remove containers
logs Fetch container output
Agent Commands:
spawn Start a Job
status Show Job status
kill Terminate a Job
review Review staged changes and execution evidence
apply Accept selected staged changes
drop Discard staged changes
inspect Open a read-only Job filesystem view
checkpoint create, list, show, delete, verify, import-base, verify-base, gc
suspend Save execution state and suspend a Job
resume Continue a suspended Job
fork Branch from staged files or VM execution state
Tools:
cache Build, publish and query caches
replay Replay an Agent trajectory (when installed)
tui Interactive Job terminal (when installed)
feature List runtime experiments
help Show command details
Safe first run¶
Host execution preserves the host filesystem view by default; only
--filesystem sandbox enables pVisor's synthetic-root/Landlock or Seatbelt
filesystem access policy. --safe stages the workspace and gives HOME
(including Codex launched from a shell) a separate copy-on-write view. Without
--stage, the changeset and Run Bundle are retained in Job storage by default
and can be reviewed, applied or dropped after exit. An explicit
--stage <PATH> retains the Job and a writable stage, keeps the changes
available for human review, and writes run-bundle.json with mode 0600.
--strict requires non-bypassable enforcement evidence for every requested
capability dimension, and otherwise fails closed before the Agent starts. Today
host, container and VM all request Network and Subprocess, and none claims
Subprocess, so --strict exits with UnsupportedPolicy on those paths. Use the
flag to verify fail-closed behavior, not to mean "a stronger sandbox is ready".
On Linux, --filesystem sandbox uses pVisor's rootless launcher with
user/mount/PID namespaces, a minimal bind-projected root, chroot and a
kernel-negotiated Landlock policy. --overlaynet-deny-all independently adds a
private network namespace; public/allowlist proxy modes remain cooperative. On
macOS, the host executor installs a generated Seatbelt policy only when
filesystem sandbox or network isolation is requested; filesystem policy and
network policy are independent. For a deny-all Run 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. Native OCI and pVisor VM executors retain the same outer Run,
OverlayFS and AgentCtl state observation.
After completion:
pvisor status --review last
pvisor fork last -- codex
pvisor apply last --all # or: pvisor drop last
fork snapshots the stopped Job's staged filesystem before launching the child.
Pass --checkpoint ID to reuse an existing logical checkpoint. 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.
To stop a running Job, use pvisor kill JOB_ID. It requests graceful termination
from the Job supervisor; check pvisor status JOB_ID for the final state. A
stopped Job can still be reviewed and applied or dropped.
Job checkpoint management¶
spawn retains its existing options and defaults. These interfaces support workspace checkpoints for stopped Jobs:
pvisor checkpoint create ./stage/task --request-id before-refactor --json
pvisor checkpoint list ./stage/task --json
pvisor checkpoint show ./stage/task CHECKPOINT_ID --json
pvisor review ./stage/task --checkpoint CHECKPOINT_ID --diff
pvisor inspect ./stage/task --checkpoint CHECKPOINT_ID -- ls
pvisor fork ./stage/task --state workspace --checkpoint CHECKPOINT_ID --stage ./stage/branch -- codex
pvisor checkpoint delete ./stage/task CHECKPOINT_ID --json
pvisor checkpoint gc ./stage/task --json
Checkpoints belong to the selected Job. The default kind is workspace, preserving
the staged upper, preimages, policy and source Attempt. Lower layers remain external
path references; this does not save process memory or guarantee that external
lowers remain unchanged. Unique ID prefixes resolve; ambiguous prefixes fail.
Creation requests can be retried with --request-id; a deleted result cannot be
captured again by reusing the same key. Forks retain hard-link references to the
source manifest, requiring parent and child stages on the same filesystem;
deletion is refused while references exist. drop JOB preserves the Job,
checkpoints and branch references. apply/drop require an explicit Job and records
confirming it has stopped. review JSON distinguishes historical execution
evidence from the currently selected file view; successful apply/drop advances
the workspace generation.
Reading or forking historical workspace checkpoints still requires the source Job lease and refuses a running source.
Full VM execution checkpoints¶
For VMs with native capture support, these commands seal CPU, RAM, devices and filesystem state:
pvisor spawn --executor vm --rootfs /path/to/rootfs --overlaynet off --stage ./stage/task -- /bin/agent
pvisor checkpoint create ./stage/task --kind execution --ram-storage compressed --request-id save-1 --json
pvisor suspend ./stage/task --ram-storage raw --request-id pause-1 --timeout 2m --json
pvisor resume ./stage/task --request-id resume-1
pvisor fork ./stage/task --state execution --checkpoint CHECKPOINT_ID --stage ./stage/branch --request-id branch-1
pvisor checkpoint verify ./stage/task CHECKPOINT_ID --json
pvisor checkpoint gc ./stage/task --kind execution --json
create --kind execution captures and continues the source. suspend succeeds only after publication and confirmed native VM termination; timeout only ends the client's wait and is not termination evidence. Retrying the same --request-id does not repeat capture. resume restores only the current suspended head, retaining the Job ID and creating a new Attempt while preserving previous records and Bundles. The original stage remains a selector for the current Attempt. Restoration retains the captured guest environment instead of inheriting the shell issuing resume.
Execution forks reject replacement commands. Selecting a historical checkpoint can leave the parent running; without one, a running parent is captured and continued, while a suspended parent supplies its head. The child owns private RAM and filesystem upper layers. --ram-storage raw|compressed applies only to new capture and defaults to compressed. resume and execution fork accept --eager-ram to read all RAM before startup; omission keeps lazy loading.
The current native profile supports Linux x86_64 and macOS ARM64 with no network devices and private RAM. Host root /, networking devices, shared memory pools, writable RAM backing and cold-page compression are outside this restore contract. spawn does not disable networking or DAX or change rootfs to enable capture; status JOB --json reports capability and blockers. Restore requires the same host boot, pVisor binary and firmware; cross-host and cross-version restore are unsupported.
When the stage is inside the workspace, capture storage is placed outside guest backing roots and recorded as Job-owned. When necessary, restored file copies also use an independent directory, with their paths retained in the Attempt record for review/apply. Capture retains the guest-visible projection, excluding stage management directories already hidden from the guest. Visible content, metadata and hard-link audits remain complete.
Suspended Jobs refuse apply/drop and workspace capture. kill JOB withdraws continuation rights while retaining checkpoint history, allowing subsequent workspace decisions. Execution checkpoints share list/show/delete entries with workspace checkpoints; deletion checks the suspended head, branch references and storage leases. Branch references are retained conservatively; Job deletion/archiving has no release interface yet. GC collects unpublished transactions, tombstones and unreferenced RAM content in this Job's stores, never published checkpoints. It is not cross-Job store-wide collection and does not clean daemon sandbox state.
Immutable base management uses checkpoint import-base JOB ROOTFS --json and checkpoint verify-base JOB BASE_ID --json. Import returns an owned rootfs path usable by subsequent ordinary spawn --rootfs. The CLI exposes snapshots through Job commands, not a standalone snapshot frontend; old stores are not automatically converted into Job checkpoints. See Job checkpoint design for implementation and acceptance.
--safe parameter preset¶
pvisor spawn --safe -- claude
pvisor spawn --safe --vm --rootfs image=my-agent-image:latest -- codex
pvisor spawn --safe -- zcode
pvisor spawn --safe --overlaynet-allow inference.example.com:443 -- zcode
--safe generates a command-line argument patch, applies it through the same
CLI parser, and then applies explicit user arguments. The preset matches agents
by executable name and automatically grants the corresponding API's HTTPS
destinations. --safe requires the selected executor to enforce isolation; it
never selects an executor. Precedence is explicit CLI > safe preset >
configuration file > ordinary defaults. It supports ordinary commands and
TOML --config; a prepared JSON --spec rejects the preset.
--safe directly requires file read, file write and network isolation
enforcement, and does not allow a silent fallback to an ordinary host process.
It adds no separate sandbox command-line option or configuration setting.
--strict remains a check over all requested capabilities, including resource
limits, and is distinct from this isolation requirement.
- macOS host: Seatbelt enforces read/write scope and allows only connection to pVisor's allocated loopback TCP proxy port; it blocks other direct IP egress and ambient host Unix sockets, keeping only the Run-local IPC that is required. The Agent uses a temporary HOME and cannot read the original home directory directly; credentials must be passed explicitly or held by Gateway. System runtime libraries and path metadata needed for startup remain readable.
- Linux host:
--saferequires rootless namespaces, a synthetic root, chroot, Landlock and a copy-on-write HOME view. Selective egress and Gateway traffic are forwarded cooperatively through the supervisor loopback proxy, and direct sockets may still bypass it; use a VM or deny-all when a non-bypassable network boundary is required. The launcher projects the host HOME through a private stage; overlay deny rules do not hide secret files at their original paths outside the workspace view. - VM: requires the existing
autonetwork boundary; safe does not select a VM automatically. - container: currently lacks a complete enforcement boundary, so
--saferefuses to start.
Isolation setup failure stops the run. --safe cannot be combined with
--overlaynet off.
The preset grants only the HTTPS destinations (port 443) in the table below; other destinations are denied by default.
| Command | Default destinations |
|---|---|
codex, bash, sh, zsh, fish |
api.openai.com, chatgpt.com, ab.chatgpt.com |
claude |
api.anthropic.com |
gemini |
generativelanguage.googleapis.com |
zcode |
api.z.ai, open.bigmodel.cn |
Unknown commands deny ordinary egress. Set grants explicitly with
--overlaynet-allow HOST:PORT (which replaces the preset list); existing deny
rules and rate limits remain in force, and Gateway capture uses explicitly
configured routes.
Without --safe, Codex state and project writes reach the host lower. With
--safe, the workspace uses the reviewable Run stage and HOME (and an explicitly
set CODEX_HOME) uses a separate private stage. HOME state changes are discarded
when the Run ends and are not part of the workspace Run Bundle.
Independently of --safe, running zcode directly on a Linux rootless host
applies a compatibility policy: it inherits the host environment and allows
direct persistent writes to existing ~/.zcode, $XDG_CONFIG_HOME (or
~/.config) and ~/.local/share/applications. When a bundled Chromium setuid
helper is found, pVisor hides it and adds --no-sandbox; it also adds
--disable-gpu. This path disables Chromium's own sandbox while retaining
pVisor's outer rootless boundary. These application-state writes bypass the
workspace stage, even with --safe. The policy matches the direct executable
name, so shell wrappers do not trigger it. The zcode-bigmodel Gateway profile
is a separate routing adapter.
The preset uses --clear-pass-env to clear configured run.pass_env;
--clear-pass-env also works on its own, and an explicit --pass-env NAME
afterward still applies. Direct Codex commands keep host environment inheritance
to preserve account and routing configuration; other commands disable
inheritance by default and receive credentials through an explicit
--pass-env NAME. Explicit CLI options can re-grant or override this. --safe
stages the workspace by default; existing container mounts and filesystem lower
layers are retained, and the project base, rootfs and executor are unchanged.
Deliver credentials to the Agent with an explicit --pass-env, or let a
configured Gateway hold the upstream key on the trusted side.
Startup prints the effective policy and override notices:
- Without
--safe, host/container selective proxies remain bypassable. - Hostname rules cannot distinguish inference, telemetry and upload APIs under the same hostname, or prevent content smuggled inside model requests.
- Wildcard rules cover the OverlayFS view; on Linux the projected HOME may still expose sensitive files at their original paths, and explicitly granted extra paths need their own protection. Filename rules cannot recognize renamed copies, secrets in source code or content in Git history, and there is no bulk-read or tool-call attribution monitoring.
- Expanding the share scope or choosing
--rootfs hostincreases the accessible data;--safecannot be combined with disabling OverlayNet.
File access rules¶
pvisor spawn --safe --mount /opt/tool:read --access '**/.ssh:deny' --access '**/.env*:warn' -- my-agent
--mount SOURCE:read|write is a host executor's explicit share, granting
read-only or persistent write access respectively; a read-only share requires
--safe or --ask. --mount SOURCE[:TARGET]:stage composes a lower layer of
the workspace view.
--access PATH-GLOB:deny|ask|warn appends file rules and does not replace the
default protections; only --clear-access clears them explicitly.
warn only warns and is not read-only; file approval covers inspection,
reading, modification and deletion within the view.
Rules match relative to the mount root: * does not cross directories, ** may
cross directories, and matching a directory covers all of its descendants.
To prevent alias bypasses on case-insensitive filesystems, matching is
case-insensitive; absolute paths, empty rules and ./.. path components are
invalid. deny takes precedence over warn. A path that matches deny is hidden
from directory enumeration, and access, creation and modification are refused;
warn allows access and prints the path to the supervisor's stderr without
printing file contents. A warning indicates a filesystem access attempt
(including metadata access), not an exact count of content reads; kernel caching
may coalesce accesses.
The default --safe deny covers .ssh and .gnupg directories at any level
and the files id_rsa, id_dsa, id_ecdsa, id_ecdsa_sk, id_ed25519 and
id_ed25519_sk. The default warn covers .env, .env.*, *.pem, *.key,
*.pub, *.p12, *.pfx, .aws/credentials, .netrc and .npmrc. Public
keys inside .ssh are hidden with the directory; public keys outside only warn.
This does not identify every private key; add rules for custom file names.
In TOML:
[overlayfs]
stage = "../stage-001"
mount = [{ source = "/opt/tool", access = "stage" }]
access = [
{ path = "**/.ssh", level = "deny" },
{ path = "**/.env", level = "warn" },
]
Explicit CLI access rules are appended after configuration and safe presets; deny takes precedence over warn. Rules are stored with the Run and overlay records.
FUSE and VM virtio-fs share the rule checks. With deny enabled, this version conservatively refuses all multiply-linked regular files and new hard links to prevent alias reads; ordinary directory renames, exchanges and deletions check the affected subtree and refuse the operation when it includes a protected file. Symlinks are resolved by the mount namespace, and file opens do not follow a final symlink to the raw backing file. The VM applies rules to the root view as well and protects the workspace's original paths and the overlay backing directories. This does not replace executor isolation: host environment reads, extra container shares and credentials outside this view still require separate controls. Backing directories should be managed by a trusted supervisor; the rules do not promise to resist races from other host processes concurrently rewriting backing files.
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, 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 the
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 the
native session. When the native session is missing, continuation fails closed.
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:
--after-step 30 --max-steps 50 leaves at most 20 continuation actions;
replay-only's budget must cover the prefix, and continuation must also leave at
least one live action.
--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. With a prompt configured, the input condition in
next-action-comparison.json is boundary_user_prompt_appended; text
similarity and tool agreement are then observational only and cannot be read
as replay fidelity under identical input.
The result schema is sandbox-playback.result/v3: phase is prepared,
replayed or continued; quality is verified or degraded;
agent_status distinguishes not_started, completed, max_steps and
failed, plus state/output locations, artifacts and an optional structured
failure. Failed results keep the logs and native trajectory already produced;
even when the OpenHands process exits 0, the result is still a failure if the
controller reports a fatal status. 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 spawn; they do not change the inner replay model
path. Replay's filesystem parameters are governed by pvisor replay --help;
do not copy spawn's --stage/--mount grammar directly.
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, 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 spawn has one canonical RunConfig. The CLI covers its common fields,
but run.inherit_env currently has no direct CLI switch. Moreover,
apply_safe_defaults currently clears environment inheritance for non-Codex
CLI commands even without --safe; the ZCode host adapter re-enables it for
direct zcode. Treat TOML inherit_env as ineffective on those CLI paths.
--config reads an explicit TOML RunConfig; --spec requires a prepared
JSON RunSpec for delegated execution and cannot be combined with other Run
overrides. pVisor does not discover a hidden project configuration file.
pvisor spawn \
--name my-agent \
--stage ../stage-001 \
--mount /opt/tool:stage \
--access '**/.ssh:deny' \
--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-destination ./capture \
-- my-agent
--record-destination writes a local pvisor_core::event::Event Journal to
events.trace.jsonl. Legacy JSONL is not supported. Journal positions define
append order and caused_by defines causal links; observed_at_unix_ms is
observation metadata, not an ordering source of truth.
The equivalent TOML is:
# host(默认)或 sandbox;与 OverlayNet 和 OverlayFS 暂存相互独立
filesystem = "host"
[run]
agent = "my-agent"
executor = "host"
command = ["my-agent"]
[overlayfs]
stage = "../stage-001"
mount = [{ source = "/opt/tool", access = "stage" }]
access = [{ path = "**/.ssh", level = "deny" }]
[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"
[record]
destination = "./capture"
Run it with pvisor spawn --config run.toml. Explicit CLI scalars replace TOML
scalars. Network and Gateway list options replace their complete configured
lists; filesystem --mount and --access entries are appended to configured
entries. The serialized [overlayfs] fields stage, mount, access and
max_size map to --stage, --mount, --access and
--overlayfs-max-size. The command after -- replaces run.command.
The size limit is checked after execution, so it does not bound peak space used
while the Agent is running.
--container-image IMAGE selects the native OCI container executor
automatically; --executor container makes the choice explicit. The transport
generates a standard OCI bundle, uses the current or explicitly supplied
compatible Linux pVisor, mounts it into the rootfs, sets the process
args, and takes the normal pvisor spawn --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.
--container-rootfs PATH supplies an existing rootfs directly; otherwise pVisor
prepares --container-image from its bundled OCI image store. The runtime must
be runc or crun; pVisor does not invoke Docker/Podman. User mounts are
repeatable TOML inline tables, for example:
pvisor spawn \
--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 example above assumes Linux x86_64. --container-platform linux/amd64
or linux/arm64 asserts the native host architecture; a matching value is
accepted and a cross-architecture value is rejected, even with a prepared
--container-rootfs. Host and VM configurations reject this option. It does
not select emulation or discover/download an executable. The injected binary
defaults to the running pVisor; use --container-pvisor-binary for an explicitly
provisioned Linux build compatible with the native architecture and rootfs ABI.
See container setup and
configuration values.
The in-process Gateway and explicit OverlayNet proxy currently require
container.network = "host", because their injected addresses are host loopback
endpoints. none mode is valid when these drivers are off; bridge requires
external CNI configuration and is currently rejected. The executor records
container isolation but does not claim complete capability enforcement.
--executor vm uses statically linked pvisor-vm and its embedded init to boot a
minimal Linux guest. --vm-ram-backing FILE ([vm].ram_backing) creates a new
private file backing live RAM. In ordinary file-backed mode, omitting it creates
an attempt-local file in the user cache, deleted on normal exit; the cold pager
below instead uses anonymous RAM without a live backing file. --vm-ram-compression
([vm].ram_compression = true) selects the PVZRAM v2 manifest and immutable
Zstd Seekable base/delta sidecar files
at startup, requiring Linux FUSE or the macFUSE kernel backend.
Use the live VM options below or Rust RunHandle::pause/resume/offload to control
the live VM. A new offload destination must be on the backing's existing filesystem.
Reclaim reports sampled residency, not guaranteed zero RAM. The file is not a
complete VM snapshot. Existing storage/control tests and compressed artifacts
establish limited implementation evidence, not end-to-end guest correctness or
production memory savings. Compressed exit still does not commit a new generation:
writes after the last resume may be discarded, leaving only the last committed head.
VM memory and control options¶
| Run option | TOML field under [vm] |
Default / purpose |
|---|---|---|
--vm-control-socket PATH |
control_socket |
Unset: owner uses /tmp/pvisor-host-<effective-UID>/jobs/<job-id>/<attempt-id>/control.sock (canonical /tmp) |
--vm-ram-backing FILE |
ram_backing |
Unset: attempt-local backing in ordinary file-backed mode; new files only |
--vm-ram-compression[=BOOL] |
ram_compression |
false; FUSE/macFUSE Seekable backing |
--vm-cold-ram-compression[=BOOL] |
cold_ram_compression |
false; Linux x86_64 local live cold pager |
--vm-ram-dedup[=BOOL] |
ram_dedup |
false; best-effort host dedup advice |
--vm-memory-pool SOCKET |
memory_pool |
Unset: external pool; Linux physical sharing or sealed compression (the latter requires userfaultfd) |
--vm-node-socket SOCKET |
node_socket |
Unset: same-host node resource service for immutable images/restored RAM |
--vm-snapshot-filesystem-pool DIR |
snapshot_filesystem_pool |
Unset: owned copies; optional host-owned immutable lower pool for Linux x86_64 no-network native checkpoints |
The three boolean options accept a bare flag (meaning true), =true or
=false; a separate false argument is not the boolean grammar. Omission
preserves the configured value, including true. For example, override dedup
without clearing other settings:
A true boolean or one of these explicit path options selects VM execution when
--executor is omitted; =false alone does not select VM. An explicit executor
is not silently replaced. Non-VM execution does not support live VM controls;
a configured control socket with an explicit host/container executor is rejected.
Path options replace only their matching field and leave omitted paths intact.
The local cold pager combines reclaim and instance-local compression in one
option; there are no separate cold-reclaim and local-compression toggles. It
conflicts with backing/FUSE compression, dedup, external pools, snapshot
capture/restore, the snapshot filesystem pool and whole-VM offload. Dedup
conflicts with either compression mode and external pools. These conflicts are
checked after config/CLI merging, so use explicit =false overrides as needed.
The filesystem pool must be host-owned, outside VM-writable roots and snapshot
stores, and on the Job's volume; setting a node socket does not grant transparent
recovery if the service fails.
Live VM Attempt controls¶
Normal controls read the canonical private owner address from the Job record:
pvisor status last --json
pvisor suspend last --vm-pause
pvisor resume last
pvisor suspend last --vm-offload --vm-ram-file /private/vm-ram/offloaded.ram
pvisor resume last --vm-load
status reports Job state. suspend --vm-pause pauses vCPUs;
--vm-offload uses stronger CPU/device quiescence and optionally writes a
host-only RAM file on the same filesystem. Pause and offload conflict;
--vm-ram-file requires offload. Live resume, including --vm-load, continues
the same Attempt without restoring a snapshot. Ordinary suspend captures an
execution checkpoint and stops the Attempt. Stopped resume restores that sealed
checkpoint. Live transition replies contain JSON status and value.
--vm-socket PATH is a debug override, requiring --vm-job-id ID and
--vm-attempt-id ID; any positional selector must match the explicit Job ID.
These flags are command-local on status/suspend/resume. --vm-control-socket
chooses the path at VM creation. Both CLI and embedded custom endpoints must be
inside canonical /tmp/pvisor-host-<UID> (usually /private/tmp on macOS), with
same-UID non-symlink 0700 ancestors and a 0600 socket. Existing sockets are
never overwritten. Default paths use jobs/<job-id>/<attempt-id>/control.sock;
the terminal owner removes discovery when the Attempt ends. Executors exclude
the whole authority root from Guest access. These are host records, not guest
discovery files. The debug override still uses the older exchange path; see
owner control limits for its cancellation
limitation and the owner protocol for framing.
VM local live cold compression¶
--vm-cold-ram-compression sets [vm].cold_ram_compression = true and selects
VM execution on Linux x86_64. The default is false; omitting the flag preserves
a configured value. The runner automatically starts an experimental userfaultfd
pager over private anonymous RAM with bounded instance-local LocalColdRamStore.
It needs neither FUSE nor a pool and is not --vm-ram-compression.
Kernel-fault userfaultfd syscall or /dev/userfaultfd permission is required;
compiled support is not authorization. Missing permission fails startup without
fallback, and pVisor changes no global sysctl. See local compression
for a user-specific ACL grant/revoke example and restricted mappings/build features.
It rejects vm.ram_backing, vm.ram_compression, vm.ram_dedup,
vm.snapshot_filesystem_pool, snapshot capture/restore and whole-VM offload.
Linux external vm.memory_pool negotiates physical sharing (private COW mappings,
no userfaultfd) or sealed compressed objects (local decode, kernel-fault userfaultfd). It requires private anonymous RAM and is mutually
exclusive with instance-local compression. Guest execution continues between short capture/recheck windows
without application participation: this is eviction/refault probing, not ordinary
pause or a true read-access heat detector. No production-density gain is promised.
VM RAM dedup advice¶
--vm-ram-dedup sets [vm].ram_dedup = true and selects the VM executor. The default is false; omitting the flag preserves a configured value. This is an explicit opt-in to cross-workload content-sharing risks, not a promise of savings. It cannot be combined with --vm-memory-pool / vm.memory_pool, --vm-ram-compression / vm.ram_compression, --vm-cold-ram-compression / vm.cold_ram_compression, or PVISOR_EXPERIMENTAL_MEMORY_POOL.
On Linux, a fresh VM with dedup enabled and no explicit vm.ram_backing uses private anonymous RAM eligible for KSM. This path does not support whole-VM offload. Explicit writable RAM backing keeps shared mappings and skips dedup advice.
The runner calls handle.advise_ram_dedup() explicitly and writes a best-effort installation report to stderr; advice failure does not stop execution. Linux advice covers ordinary private anonymous RAM and restored private COW mappings. Live MAP_SHARED RAM is skipped without mapping conversion; macOS reports unsupported for otherwise eligible mappings. accepted_bytes means advice was accepted for those ranges, not merged bytes, savings or an enabled KSM scanner. No global KSM settings change and no new service is required. See current integration and evidence for eligibility, snapshot ownership and validation limits.
VM rootfs and executor boundaries¶
--rootfs image=IMAGE selects this executor and pulls an
OCI/Docker image directly, without invoking Docker, Podman or Buildah. When no
explicit rootfs or image is supplied, Linux uses the host / through virtiofs
and OverlayFS by default, preserving the host runtime, PATH and HOME without
pulling an image. On macOS, supply a Linux rootfs or image explicitly.
Manifests and layer digests are verified, the host architecture selects
linux/arm64 or linux/amd64, and the unpacked rootfs becomes the shared
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> uses a
prepared directory and --rootfs image=<PATH> uses an OCI image or image path;
the three are mutually exclusive, and host rootfs is rejected on macOS. This is
the unified rootfs syntax; --mount SOURCE[:TARGET]:stage composes extra lower
layers for the guest workspace, and the current workspace is the implicit bottom
layer. Read/write explicit host shares are currently supported only by the host
executor. Workspace changes enter the named stage or default Job storage and are
retained after exit; other writes to the VM root use a temporary upper and are
discarded when the VM exits.
The merged rootfs is guest /, and /workspace becomes the guest cwd. On both
Linux and macOS, pvisor-vm 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. Linux static musl and
macOS ARM64 product builds embed the compressed guest kernel, require no firmware
shared library at runtime. Linux static builds reject --vm-library-dir;
macOS product builds use the embedded kernel regardless of that directory. Source builds prepare the repository-maintained firmware through
mise run build or mise run fw -- build. Runtime resolution only uses local
firmware and fails when it is missing; it neither downloads nor compiles it.
For standalone macOS Cargo builds without a build-time kernel input, --vm-library-dir selects an existing firmware directory. 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
despite the guest-kernel isolation the first OCI-image version must not be
treated as a hostile multi-tenant boundary.
On host/container execution, the four visible OverlayNet policy flags and
Gateway capture automatically enable the proxy driver. --safe stages the
workspace by default and --mount adds explicit lower layers; see
Staging and storage for write destinations. 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. VM 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. Until
pVisor can safely materialize a complete merged-vs-base diff, a composed Run
rejects a subsequent pvisor apply. Selective host/container network rules
apply to traffic through the explicit proxy. Host deny-all uses a namespace or
Seatbelt to block direct egress; containers can use --container-network none
for offline execution. On a pVisor VM, auto uses smoltcp IPv4 TCP/DNS and
off leaves the guest offline; deny-all still permits configured internal
Gateway routes. See network boundaries for the
scope of each path.
Use a sealed rootfs¶
Prepare a quiescent, independent Linux rootfs into a private store, then replace
BASE_ID with the returned base.id. Keep the store outside the workspace,
stage, every guest-visible path and writable VM backing/control paths.
pvisor cache prepare-rootfs --rootfs /srv/linux-rootfs --base-store /srv/pvisor-bases
pvisor spawn --vm --rootfs-base-store /srv/pvisor-bases --rootfs-base BASE_ID -- /bin/sh
Local preparation does not use a cache service. Successful output contains
schema_version, prepared_rootfs (canonical store and base.id) and the
independent rootfs path. Initial preparation includes copying, full content
and metadata verification, and a persistent pin. Later ordinary CLI runs have
the Job owner authenticate the object, acquire a lifetime lease and attach
the Immutable physical lower without sealing it again. The workspace remains
live by default; an explicit sealed workspace can select
a fixed generation.
--rootfs-base-store and --rootfs-base must be supplied together. They conflict
with --rootfs and OCI image sources; explicit host/container executors and JSON
--spec reject them. TOML uses vm.prepared_rootfs.store and
vm.prepared_rootfs.base.id with explicit [run] executor = "vm". A recorded
vm.rootfs must match the authenticated result. The store pin survives process
exit and GC; there is currently no prepared-entry removal API. External store
edits are unsupported. This source currently rejects execution checkpoint
capture/restore; workspace logical checkpoints remain independent.
Apply protection on ordinary directories, OCI or lazy projections does not
grant this physical ownership.
On Linux x86_64, a fresh native launch with an authenticated prepared rootfs
automatically requests read-only FLUSH elimination for that native child when
the host environment omits PVISOR_VM_FS_READONLY_FLUSH_ELISION; an explicit
0 preserves the original behavior. The guest must negotiate support, and only
eligible local Immutable read-only fds skip backend FLUSH. Older guests and
ineligible fds retain the original FLUSH. SDK calls without a selected and
authenticated prepared rootfs (including calls supplying only a with_rootfs_base
owner), and macOS retain their previous behavior when the variable is absent. This
default does not enable data-page caching, negative caching, WB or DAX.
Use a sealed workspace¶
Prepare a quiescent project into a separate private store, then run from the
project directory. Use the rootfs preparation result for ROOTFS_ID and the
workspace preparation result for WORKSPACE_ID.
pvisor cache prepare-workspace --workspace /srv/project --base-store /srv/pvisor-workspace-bases
cd /srv/project
pvisor spawn --vm --rootfs-base-store /srv/pvisor-bases --rootfs-base ROOTFS_ID \
--workspace-base-store /srv/pvisor-workspace-bases --workspace-base WORKSPACE_ID \
--stage /srv/pvisor-stages/review-1 -- /bin/sh
Preparation returns schema_version, prepared_workspace (canonical store
and base.id) and an independent workspace path. Initial preparation copies
and verifies contents and metadata. A persistent pin survives command exit and
GC; later Job owners authenticate the workspace catalog again and acquire a
lifetime lease without sealing the source again.
Reads use the selected fixed generation; subsequent host project edits do not
enter the current Run's snapshot read view. Writes enter a fresh stage. Ordinary
pvisor apply still targets the original live project, checking the snapshot
baseline for selected written paths and retaining conflicts. This does not
provide serializable transaction checks over every read path. Redirecting with
apply --target is unsupported.
Supply both workspace arguments together. They require prepared rootfs, a
native VM, a fresh stage and one workspace lower. TOML uses
overlayfs.prepared_workspace.store and overlayfs.prepared_workspace.base.id.
Explicit host/container executors, JSON --spec, compose, logical workspace
checkpoint fork/restore and execution checkpoint capture/restore are rejected.
Creating and reading workspace checkpoints remain independently available.
Keep the two stores independent of each other
and outside the project, stage, guest-visible paths and writable VM
backing/control paths. External store edits are unsupported. This entry inherits
the scoped prepared-rootfs read-only FLUSH default, without
automatically enabling data-page caching, negative caching, WB or DAX. Set
PVISOR_VM_FS_READONLY_FLUSH_ELISION=0 to preserve the original FLUSH.
Run project discovery¶
The current directory is the default project association. --mount identifies
additional host lower layers and, optionally, their Agent-visible paths. Each
Run receives an independent directory under pVisor's default records root. If
that root would fall 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
~/.pvisor/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/
├── merged/
├── checkpoints/
├── lease.lock
├── control.sock # while a live OverlayFS Run is available
├── .capture/ # when OverlayNet/Gateway is enabled
└── events.trace.jsonl # when --record-destination is set
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 capture artifacts.
Shared image file cache¶
pvisor-daemon serve --cache runs the independent OCI file service in the foreground;
pvisor cache prepare IMAGE, pvisor cache list DIGEST [PATH],
pvisor cache stat DIGEST PATH and pvisor cache read DIGEST PATH reach it
through PVISOR_CACHE_SERVER. pvisor cache publish IMAGE explicitly publishes
to a writable filesystem/S3 backend. The default is
the pvisor/cache.sock Unix socket under the user cache directory. The server
accepts --cache-image-store DIR to name an existing OCI store. File reads support
ranges and SHA-256 verification.
VM image runs automatically probe the default socket; when the service is
available they attach the remote image directly to the virtio-fs service as a
read-only lower without an intermediate host FUSE mount, fetch 1 MiB blocks on
demand, and cache them persistently. A missing or stale default socket
falls back to local OCI preparation. An explicitly configured server must work;
PVISOR_CACHE_SERVER=off forces local preparation. Explicit rootfs directories
and the native container executor keep their current behavior. See the
shared image cache protocol for the full protocol,
limits and SSH remote access.
Enable the daemon-owned experimental pool with pvisor-daemon serve --sandbox --memory-pool; its detached memory-pool --directory DIR component uses daemon-provisioned private configuration. Native pvisor spawn --vm-memory-pool SOCKET remains an explicit runtime option with platform/protocol limits, not a service launcher.
Physical/legacy pool loss fails dependent VMs; Linux sealed-compressed clients retain local recovery and stop new publication. Select it with daemon --memory-pool-mode compressed. See daemon pool activation and budgets; historical macOS compressed-pool results retain their version scope.