pvisor command reference¶
Job is pVisor's primary user-facing object. pvisor run creates a Job; the other
flat commands operate on it directly, without adding 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 Execution
(run, status, kill), Changes (review, apply, drop, inspect),
Checkpoints (checkpoint, suspend, resume, fork) and Tools
(installed replay/tui companions, 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.
Host Job service¶
Built-in Job commands (run, status, kill, suspend, resume, fork,
checkpoint, inspect, review, apply, drop) submit typed requests through
Host AgentCtl. The first request starts a persistent same-user listener under
/tmp/pvisor-host-<effective-UID> (using canonical /tmp, usually /private/tmp
on macOS); later requests reuse it. You do not need to start a service or pass
endpoint options for ordinary persisted Jobs. Bare pvisor still displays help;
pvisor -- COMMAND follows default execution through the same Job service.
The frontend retains its terminal and launches a listener-authorized worker;
stdio and the private worker channel use Unix SCM_RIGHTS descriptor transfer.
Job service/internal-worker JSON uses shared runtime/host_transport.rs
async/sync newline framing with a 1 MiB JSON limit excluding the delimiter;
FD marker bytes are separate transport records, not JSON. The listener checks
same-UID kernel credentials and agrees on Host version 1, the internal Job
ticket schema, package version and executable content digest before
admitting stdio/commands. The typed JobCommand payload contains internal CLI
DTOs tied to that exact schema/build, not a stable public API. It is separate from daemon sandbox/pool ownership and the independent image cache.
Guest AgentCtl Hello/Sync remains a separate cooperative channel: its token
cannot authorize host Job or VM operations.
Drain active requests and stop an old listener with its old binary before upgrading. A new client refuses an incompatible live listener; there is no legacy fallback. Lost responses and cancellation can leave effects ambiguous; requests are not automatically retried. See Host and Guest AgentCtl for ownership, protocol and validation limits.
Experimental features¶
List runtime features without starting or contacting the Host Job service:
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 run; 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 run 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¶
Top-level pvisor commands operate native Jobs. Invoke pvisor-daemon directly for sandbox lifecycle and optional pool ownership; use pvisor-cache independently for OCI caching.
Add --memory-pool to pvisor-daemon serve to enable its default-off detached pool component. pvisor-cache prepare/publish/serve/list/stat/read remain independent. 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 daemon and cache entry points 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 run -- 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 run, 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.
For your first run, copy the smallest loop:
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 run --stage ../run-stage -- codex
pvisor run --safe --mount /opt/zcode:read --mount /var/lib/zcode:write -- zcode
pvisor run --access '**/.ssh:deny' -- zcode
pvisor run --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.
Jobs:
run Start a Job
status Show Job status
kill Terminate a Job
suspend Save execution state and suspend a Job
resume Continue a suspended Job
fork Branch from staged files or VM execution state
checkpoint Create, list, show, delete, verify, import-base, verify-base, gc
Filesystems:
inspect Open a read-only Job filesystem view
review Review staged changes and execution evidence
apply Accept selected staged changes
drop Discard staged changes
Extensions:
service Native service lifecycle and installed daemon/cache/memory-pool companions
replay Replay an Agent trajectory (when installed)
tui Interactive Job terminal (when installed)
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¶
run 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 run --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. run 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 run --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 run --safe -- claude
pvisor run --safe --vm --rootfs image=my-agent-image:latest -- codex
pvisor run --safe -- zcode
pvisor run --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. Note that:
- 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 run --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.
--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. 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, 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. 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 run \
--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 run --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 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.
--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 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 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: CLI creates /tmp/pvisor-host-<effective-UID>/vm-<UUID>.sock under 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: experimental external pool; Linux shares physical pages without 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¶
Every native VM Attempt gets a host-only control endpoint automatically,
including embedded runs without retained Job storage. CLI endpoints live directly
under the private Host service root. pvisor run prints its exact identity to
stderr:
pVisor live VM options: --vm-socket /tmp/pvisor-host-1000/vm-EXAMPLE.sock --vm-job-id run-EXAMPLE --vm-attempt-id attempt-EXAMPLE (status; suspend JOB --vm-pause/--vm-offload; resume JOB --vm-load)
Copy the socket, Job ID (the internal Run ID) and Attempt ID from that line into another host terminal; the following example values must be replaced with that live identity:
pvisor status --vm-socket /tmp/pvisor-host-1000/vm-EXAMPLE.sock --vm-job-id run-EXAMPLE --vm-attempt-id attempt-EXAMPLE
pvisor suspend run-EXAMPLE --vm-socket /tmp/pvisor-host-1000/vm-EXAMPLE.sock --vm-job-id run-EXAMPLE --vm-attempt-id attempt-EXAMPLE --vm-pause
pvisor resume run-EXAMPLE --vm-socket /tmp/pvisor-host-1000/vm-EXAMPLE.sock --vm-job-id run-EXAMPLE --vm-attempt-id attempt-EXAMPLE --vm-load
pvisor suspend run-EXAMPLE --vm-socket /tmp/pvisor-host-1000/vm-EXAMPLE.sock --vm-job-id run-EXAMPLE --vm-attempt-id attempt-EXAMPLE --vm-offload --vm-ram-file /private/vm-ram/offloaded.ram
pvisor resume run-EXAMPLE --vm-socket /tmp/pvisor-host-1000/vm-EXAMPLE.sock --vm-job-id run-EXAMPLE --vm-attempt-id attempt-EXAMPLE --vm-load
The seven live VM flags are command-local and appear under Live VM in command help:
| Command | Accepted live VM flags |
|---|---|
status |
--vm-socket PATH, --vm-job-id ID, --vm-attempt-id ID |
suspend |
The three identity flags plus --vm-pause, --vm-offload, --vm-ram-file PATH |
resume |
The three identity flags plus --vm-load |
Place them after the supported command. Root-prefixed live VM flags and live VM
flags on other commands are rejected; status accepts no live action flags.
The identity options --vm-socket PATH, --vm-job-id ID and
--vm-attempt-id ID are required together, even for
live status; stale or mismatched identities are rejected. suspend and
resume require a positional Job selector matching --vm-job-id, not last
or a stage path. Only status, suspend --vm-pause / --vm-offload, and
resume --vm-load accept this live control mode. Pause and offload are mutually
exclusive. These flags do not change ordinary persisted-Job suspend/resume:
without them, those commands use execution checkpoint capture/restoration.
--vm-offload accepts optional --vm-ram-file PATH: omit it to use the existing
backing, or choose a new, guest-inaccessible path on the same filesystem. Wait
for success before using the published file. --vm-load selects
HostVmCommand::Resume, mapped to RunResume; there is no Load wire operation.
It reloads/unpauses the same live Attempt without eager RAM prefaulting. It
neither restarts a process nor restores a persistent snapshot. --vm-pause
stops vCPUs, not the stronger CPU/device quiescence used by offload.
Successful live control replies are JSON on stdout containing HostVmResult
fields status and value. Core's host_protocol owns HostVmCommand and
HostVmResult; pvisor::host_vm_exchange exchanges
AgentCtlHostRequest<HostVmCommand> / AgentCtlHostResponse<HostVmResult> for
embedded callers. The endpoint
wire wraps results in version-1 Host envelopes with a correlated request_id;
typed Host errors are nonzero CLI failures, not necessarily JSON on stdout. Transport/connect and parsing failures also exit
nonzero. Non-VM controls are explicitly unsupported, not a process-signal
fallback. The endpoint is removed when the Attempt ends and is separate from
the staged Job's control discovery link.
To choose a stable CLI path, use the private service root. This Linux example
assumes effective UID 1000; replace the root with your effective UID and
canonical temporary path (usually /private/tmp on macOS):
install -d -m 0700 /tmp/pvisor-host-1000
pvisor run --executor vm --vm-control-socket /tmp/pvisor-host-1000/ctrl.sock -- /bin/sleep 600
CLI custom sockets must be directly in the canonical private Host service
root; arbitrary private parents are rejected. The root must be a non-symlink
directory owned by the effective UID with mode exactly 0700; existing socket
paths are never overwritten. The socket has mode 0600 and accepts only
same-UID peers. --vm-control-socket chooses a path when creating a VM;
--vm-socket addresses an existing live VM. Embedded callers have a separate
custom-parent API with private-parent validation. Host authority endpoints are
excluded from guest access, including host-rootfs VMs; do not expose the service
root through guest mounts or writable roots. These are not guest discovery files.
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 maps physical pool pages privately: reads
retain sharing and writes use COW, without 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 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> 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 builds
embed the guest kernel and require no firmware shared library at runtime, and
they reject --vm-library-dir. macOS wheels install libkrunfw.5.dylib beside
pVisor; macOS source runs otherwise download the pinned official release into a
SHA-256-verified platform cache, where /usr/bin/cc turns the prebuilt kernel
bundle into the required dylib. On macOS, --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.
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.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-cache serve 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 --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 mount the remote image as a read-only FUSE lower, 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 --memory-pool; its detached memory-pool --directory DIR component uses daemon-provisioned private configuration. Native pvisor run --vm-memory-pool SOCKET remains an explicit runtime option with platform/protocol limits, not a service launcher. Pool loss fails dependent VMs. See daemon pool activation and budgets; historical macOS compressed-pool results retain their version scope.