Repository navigation
cli: take the downloads and the control session off the old supervisor (SL3.10a) - #1491
Merged
Pedro Henrique Penna (ppenna) merged 2 commits intoNov 12, 2026
Conversation
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/slim-sl3-4b-sandbox-run
branch
from
November 11, 2026 19:30
279679d to
b09ec61
Compare
Contributor
This pull request changes existing testsIt modifies or removes 11 existing test asset(s), listed below. Review each with care: a change to a test can hide a regression that the test caught. See doc/contribute.md#changes-to-existing-tests. Test code
Relocated unchanged: 0; added: 11. Compared the merge base |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Critical shutdown and image-handoff races can kill OpenVMM during guest filesystem flushing or bypass checked-inode locks through pathname replacement, allowing concurrent image writes.
2 open issues
What changed in this PR
This PR advances #1112 by moving CLI downloads and control-session attachment off the legacy supervisor. The supplied changes also include the stacked #1490 migration of sandbox run to the managed VM API.
Changes:
- Spawn download tools directly and attach cold-run sessions through verified process references.
- Run sandbox workloads through managed sessions, with shutdown, cleanup, and protected-path checks.
- Update tests, CI scenarios, and documentation for the managed sandbox behavior.
| File | Description |
|---|---|
scripts/test-layout-allowlist.json |
Removes obsolete test-layout allowance. |
scripts/test_microvm_tests.py |
Updates sandbox acceptance fixtures and regression cases. |
scripts/nvx_tools/sandbox_ram_upper_tests.py |
Checks managed RAM-upper runs and forwarded reports. |
scripts/nvx_tools/sandbox_lifecycle_tests.py |
Updates unavailable-identity expectations. |
nvx-rs/tests/cli/validation.rs |
Adjusts execution-option validation coverage. |
nvx-rs/tests/cli/sandbox_run.rs |
Adds managed sandbox-run integration coverage. |
nvx-rs/tests/cli/openvmm.rs |
Adds managed-run expectations and output normalization. |
nvx-rs/tests/cli/one_shot.rs |
Removes superseded sandbox one-shot cases. |
nvx-rs/tests/cli/lifecycle.rs |
Adds protected-path tests and shared helpers. |
nvx-rs/tests/cli/dry_run.rs |
Updates expected managed sandbox commands. |
nvx-rs/tests/cli/download.rs |
Adapts download and provisioning tests. |
nvx-rs/tests/cli.rs |
Registers run tests and updates lifecycle expectations. |
nvx-rs/src/host/vm.rs |
Makes foreground child access private. |
nvx-rs/src/host/tests.rs |
Adds a test-only spawn-lock guard. |
nvx-rs/src/host/sandbox/tests/specification.rs |
Replaces boot-token expectations with mapping tables. |
nvx-rs/src/host/sandbox/tests.rs |
Updates sandbox launch-contract tests. |
nvx-rs/src/host/sandbox/prepared.rs |
Removes private one-shot preparation. |
nvx-rs/src/host/sandbox/plan/tests.rs |
Removes obsolete one-shot rendering checks. |
nvx-rs/src/host/sandbox/plan.rs |
Removes private boot-token delivery paths. |
nvx-rs/src/host/sandbox/options/tests.rs |
Tests run execution options and managed shares. |
nvx-rs/src/host/sandbox/options.rs |
Allows execution options for sandbox runs. |
nvx-rs/src/host/cli/session/tests.rs |
Tests attachment waits against a real process. |
nvx-rs/src/host/cli/session.rs |
Uses verified processes and extracts request validation. |
nvx-rs/src/host/cli/sandbox/tests.rs |
Updates option tests and relocates overlap coverage. |
nvx-rs/src/host/cli/sandbox/options.rs |
Updates help for managed run options. |
nvx-rs/src/host/cli/sandbox/mod.rs |
Routes managed runs and checks protected paths. |
nvx-rs/src/host/cli/sandbox/lifecycle/start/tests.rs |
Checks share overlap before launch. |
nvx-rs/src/host/cli/sandbox/lifecycle/start.rs |
Implements temporary managed workload runs. |
nvx-rs/src/host/cli/sandbox/lifecycle/mod.rs |
Exposes the managed run implementation. |
nvx-rs/src/host/cli/run.rs |
Passes the verified process to session attachment. |
nvx-rs/src/host/cli/download/github/tests.rs |
Locks tool spawns and checks missing-curl diagnostics. |
nvx-rs/src/host/cli/download/github.rs |
Spawns curl directly. |
nvx-rs/src/host/cli/download/git.rs |
Spawns git directly. |
nvx-rs/src/host/cli/download/archive/tests.rs |
Protects archive tool spawns in concurrent tests. |
nvx-rs/src/host/cli/download/archive.rs |
Spawns tar directly. |
nvx-rs/README.md |
Documents managed sandbox runs and path protections. |
doc/usage.md |
Updates sandbox options and execution behavior. |
doc/ci.md |
Describes managed smoke-test expectations. |
.github/workflows/run-nvx-microvm-tests.yml |
Adapts sandbox probes to nvx-init execution. |
Review effort: Balanced
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/slim-sl3-4b-sandbox-run
branch
from
November 11, 2026 19:48
b09ec61 to
5d0f26d
Compare
Base automatically changed from
agents/slim-sl3-4b-sandbox-run
to
feature/nvx-refactor
November 11, 2026 20:13
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/slim-sl3-10a-downloads-session
branch
from
November 11, 2026 20:23
e6b2dec to
5fda186
Compare
This was referenced Nov 11, 2026
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/slim-sl3-10a-downloads-session
branch
from
November 11, 2026 23:05
5fda186 to
2c4bcc6
Compare
This was referenced Nov 11, 2026
The download started its tools with supervisor::spawn_command, which holds the old supervisor's spawn lock while it creates a process, so that on Windows the tool inherits no handle of a VMM that another thread of the process starts meanwhile (#966). The CLI is to stop using the old supervisor, and needs no lock for its tools: run, sandbox run and sandbox start download before they launch, and no CLI command starts a VM on another thread while it downloads. tar, git and curl now start with Command::spawn, with the same standard streams, environment and working directory as before. Their errors stay as they were: a tool that does not run fails the download as unavailable, naming the program; a tool that fails reports its standard error; and the CLI checks for Ctrl-C once a tool exits, which reaches the tools as before, since they still share the CLI's console and process group. The library's tests, though, run the download beside tests that launch stand-in VMs, so #966's protection moves into them: each archive and GitHub test that runs tar or curl holds the tests' spawn lock, which host::tests::hold_spawns returns, while it runs them, and the archive tests' helper that packages a release spawns its own tar with host::tests::spawn_locked, which therefore builds wherever the CLI does. A new GitHub test checks the error of a curl that does not run, which the changed spawn produces. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A managed cold run waits for its control endpoint with Session::connect_launched_cancellable, which ends the wait once OpenVMM exits, rather than at its timeout. It took the old supervisor's child type, spawn::Launched, which host::vm::Launched lent it through child_mut, and polled that child's status. It now takes the verified process of the run's host::vm::Launched, beside the endpoint and capability that the launch supplies, and asks that process whether it has exited. The process is the same OpenVMM, held through its pidfd or process handle, so the wait notices its exit as the child's status did, without reaping it: the foreground still reaps it through wait_status. A check that fails still keeps the wait going until the timeout. child_mut becomes private to host::vm, whose wait_status alone uses it, and the session's comments no longer name host::instance. sandbox exec and sandbox stop attach through Session::reattach, which verifies a saved VM through host::vm, and do not change. A new test gives the wait a process that runs and then exits: the wait goes on while the process runs, and ends as a closed endpoint once it exits, rather than at its timeout, on Linux and Windows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/slim-sl3-10a-downloads-session
branch
from
November 12, 2026 00:41
2c4bcc6 to
6262d14
Compare
Pedro Henrique Penna (ppenna)
marked this pull request as ready for review
November 12, 2026 00:41
Pedro Henrique Penna (ppenna)
merged commit Nov 12, 2026
14ca267
into
feature/nvx-refactor
86 checks passed
Pedro Henrique Penna (ppenna)
deleted the
agents/slim-sl3-10a-downloads-session
branch
November 12, 2026 00:59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Part of #1112 (SL3.10a: the CLI's downloads and session no longer use the old supervisor or instance).
What and why
The CLI is moving off
nvx's old supervisor and instance layer. This PR does SL3.10a's Scope and no more:download::{archive, git, github}):tar,gitandcurlstart withstd::process::Command::spawninstead ofhost::supervisor::spawn_command, which only held the old supervisor's Windows spawn lock around the same spawn (nvx-rs: keep processes spawned during a launch from holding its VMM's log on Windows #966). Their standard streams, environment, working directory and errors are unchanged, and the CLI still checks for Ctrl-C once each tool exits; the tools still share the CLI's console and process group, so Ctrl-C reaches them as before.Session::connect_launched_cancellable, with which a managed coldrunwaits for its control endpoint, takes the verifiedProcessof the run'shost::vm::Launched, beside the endpoint and capability that the launch supplies, instead of the supervisor-owned child (host::spawn::Launched), and ends the wait once that process has exited, as the child's status ended it.host::vm::Launched::child_mut, which lent that child to the session, becomes private tovm, where onlywait_statususes it, and the session no longer mentionshost::instance.sandbox execandsandbox stopalready attach throughSession::reattach, which verifies a saved VM throughhost::vm, and do not change.Not in this PR (per #1112): download formats, validation, retries, repositories or authentication; NVXC messages, exec semantics or stop policy; a process supervisor or command wrapper; new commands, flags or output.
#966 stays in the tests. The Windows spawn lock stays private to
host::vm::spawn, and a caller keeps its own spawns out of a VM's start (#1076). The CLI does so in production by downloading before it launches:run,sandbox runandsandbox startdownload first, and no command starts a VM while it downloads, so its tools spawn without the lock. The library's tests, though, run the download beside tests that launch stand-in VMs, so they keep #966's protection themselves: each archive and GitHub test that runstarorcurlholds the tests' spawn lock, from the newhost::tests::hold_spawns, while it runs them, and the archive tests' helper that packages a release spawns itstarwithhost::tests::spawn_locked. No production code takes the lock.Holding the lock delays the stand-ins' own spawns while a tool runs, about a second for the retried request of
the_last_response_to_a_retried_request_counts. Before #1495 (SL3.7b follow-up), that madehost::cli::snapshots::capture::tests::a_capture_ack_and_successful_output_without_timely_exit_are_stopped_but_never_finishedfail on Windows whenever it ran beside that test (3 of 3; it passed alone and beside the archive tests), since its capture's one-second deadline also covered its fake VMM's start. In five full library runs without #1495, it was the only test that failed: nothing else fails under the lock. #1495 starts that fake before the capture, and merged intofeature/nvx-refactorasac3bbefb7d4, on which this PR now builds: here the capture test passes in the full library run beside the download tests (see Validation), as it did beside the retried request (3 of 3) and in two full library runs with #1495's head (9b816f7e523, the same patch) applied before it merged.First implementation. Enrique Saurez (@esaurez)'s
esaurez/private-sl310a-0325(05a7eb2, acd18ef, on an older base) is replayed as two commits that keep his authorship, adapted to the current session code (the cancellable attachment of #1460), with these corrections:child_mutbecomes private, and its docs and the field's docs say what remains; the download tests hold the spawn lock while they run their tools, and the archive tests' helper spawns its owntarunder it, rather than spawning bare; a new test checks the session's wait on a real process; and his second new GitHub test, of a failingcurl, is left out, since this change does not touch that path.Size. 11 core lines (estimate 30, stop 60):
session.rs4,archive.rs2,run.rs2,git.rs1,github.rs1,vm.rs1. 5 counted files (archive.rs,git.rs,github.rs,session.rs,run.rs), andvm.rs, whose edit only makeschild_mutprivate and updates two doc comments (6 if it counts). The public API,BOOT_CONTRACT_VERSIONandCONTROL_CONTRACT_REVISIONdo not change.Validation
Merged at
6262d145367(onfeature/nvx-refactor1b902ac16a5, after #1486, #1487 and #1489): the rebase had no conflicts (with rerere off; onlyrun.rsoverlapped, in other hunks),rebase_check.py verifypassed, andgit range-diffshows both commits unchanged (=), with the patch IDs that Copilot reviewed ate6b2dec437f. CI run 43129157648 passed, 34 jobs and 9 skipped, KVM, MSHV and WHP included. Locally on Windows, the Format and Lint commands of check-nvx-rs, 103 unit tests of the downloads, session, capture andhost::tests, and theclitarget (110) passed. Every download of the CLI still runs before its command launches a VM (run,sandbox startandsandbox run, restored starts included), as the #966 paragraph above requires.At
2c4bcc67545, onfeature/nvx-refactorf834bcb9672: the rebase fromd4edeeb9ab9over #1495, #1488, #1494 and #1492, none of which touches this PR's files, had no conflicts (with rerere off),rebase_check.py verifypassed, andgit range-diffshows both commits unchanged (=), with the same patch IDs and still signed; the diff over the base is byte-identical.cargo fmt --check; Clippy-D warnings(--all-targets --all-features,--lib,--lib --no-default-features,--lib --no-default-features --features serde,--lib --bins --features cli,--lib --bins --no-default-features --features testing);cargo test --all-features;cargo test;--lib --no-default-featuresbuild and tests; bundled-artifacts test;cargo docwith-D warnings; MSRV 1.89; runtime adapter protocol-only and host-onlycli110,security_checklist11, and every other targetcheck_test_layout.py;nvx.py verifyLinux and the microVM scenarios were not rerun locally at
2c4bcc67545, whose commits are unchanged; their results at5fda1863414follow.At
5fda1863414, onfeature/nvx-refactord4edeeb9ab9, where #1490 merged: the rebase from #1490's branch (git rebase --onto origin/feature/nvx-refactor 279679d1839) had no conflicts, andgit range-diffshows both commits unchanged, with the same patch IDs and still signed.rebase_check.py verifyreports only the stacked commits that merged upstream, as7bc238eb3d0and9086b4a6a78with the same patches and asd4edeeb9ab9with #1490's review fixes, none of which touch this PR's files.cargo fmt --check; Clippy-D warnings(--all-targets --all-features,--lib,--lib --no-default-features,--lib --bins --features cli,--lib --bins --no-default-features --features testing);cargo test;--lib --no-default-featuresbuild and tests; bundled-artifacts test;cargo docwith-D warnings; MSRV 1.89; runtime adapter protocol-only and host-onlycargo test --all-featurescli110); lib 1465 passed and 1 failed, the capture test above, which #1495 has since fixedcargo fmt --check; Clippy--all-targets --all-features -D warnings; the download and session unit tests (93);cargo test --all-features --test cli(119, the Ctrl-C tests ofinterruptsamong them); macOS checknvx.py test-microvm --backend kvm:managed-exec-config,sandbox-lifecycle,sandbox-ram-upper,sandbox-blocks,managed-lifecycle,filesystem-comma-pathscheck_test_layout.pyAt
e6b2dec437f, on #1490's branch, with the same commits: the same Windows results (lib 1464 and the capture test), and on Linux the checks above (cli118) and four of the scenarios.At the first head (
0c4cb9c0d99, on1008a3e0056), with the same production code:guest::unit tests that fail on nvx-kvm-test at the base too (lib 2268 passed);nvx.py check-guest-binariesfails 26 of them, in a guest-only build that compiles none of the changed filespython3 scripts/public_api.py, runtime adapter guest-only, guest binaries build and testslaunched_waits_end_once_their_process_exitsfails when the exit check always finds OpenVMM exited (the wait ends while it runs) or never does (the wait lasts its 60 s timeout);a_missing_curl_reports_the_program_and_query_contextfails when the spawn's error loses its context (codeio, notunavailable)check_test_layout.py,check_markdown_links.py; validate-nvx on Windows (compileall, the 22scripts/test_*.pymodules, 1834 tests,nvx.pyhelp)nvx.py test-microvm --backend kvm(nvx-kvm-test, Azure Linux 3, virtual machine):managed-exec-config,sandbox-lifecycle,sandbox-proxy-only,sandbox-ram-upper,sandbox-blocks,managed-lifecycle,filesystem-comma-paths,l3-l4-egress-policynvx.py test-microvm --backend whp(Windows 11 developer machine): the same butmanaged-lifecyclenvx-rs run --cmdline "nvx_mode=managed nvx_identity=65534:65534", Ctrl-C after 15-20 s, KVM and WHPInterruptednvx_identity=1001:1001, which the guest lacks), KVM and WHPnvx-rs downloadinto a checkout ofc3ff9d6cf527, KVM and WHPv0.1.0-dev.c3ff9d6cf527(271 and 142 MiB packages)Interrupted; no staging directory, nothing installed, nocurlleftHTTPS_PROXYerror[unavailable]: GitHub release query failed: curl: (7) ...The KVM runs used OpenVMM
14dc4116and the guest kernel and initramfs built on the host from this branch, and the Ubuntu distro layer of CI run 42994995631 (a53a2626d6c, the same guest inputs); the WHP runs used that run's Windows OpenVMM and guest artifacts. On Windows, the base binary (1008a3e0056) gave the same results for the downloads, Ctrl-C included, and for both managed runs.Observed at the base too, not changed here: a managed blockless
runthat Ctrl-C ends printsVM graceful stop failed: EndpointClosedand loses OpenVMM's outcome report, on KVM and WHP, with this branch's binary and the base's alike; it is recorded as a follow-up.Left to CI: MSHV, and the remaining microVM scenarios.
Changes to existing tests (
tests-modified)check_test_changes.py --base origin/feature/nvx-refactorlists 11 changes, 11 added items, none relocated:host::cli::download::archive::tests::archive(helper): spawns thetarthat packages a test release withcrate::host::tests::spawn_lockedinstead ofsupervisor::spawn_command, under the same lock over the process's creation.host::cli::download::archive::tests::{gzipped_packages_extract_once_their_names_and_checksums_pass, zip_packages_extract_with_the_tar_of_windows, packages_whose_checksums_fail_are_refused, packages_with_symlinks_are_refused, archives_that_tar_cannot_read_are_refused}: each holdshold_spawns()while it runs, sinceextractrunstarwithout the lock now; their packages, calls and assertions do not change.host::cli::download::github::tests::{queries_and_downloads_carry_the_token_to_the_origin_of_the_api_alone, requests_without_a_token_carry_none, redirects_resolve_relative_locations_against_the_urls_of_their_requests, the_last_response_to_a_retried_request_counts}: each holdshold_spawns()while it runs, since the client runscurlwithout the lock now; their servers, requests and assertions do not change.host::tests::spawn_locked(helper): also builds with theclifeature on hosts other than Linux and Windows, since the archive tests, which macOS builds too, now use it; its body does not change.Every other test of the area is kept unchanged,
Curl::plainstays test-only indownload/github/tests.rs, anddownload/tests.rsdoes not change. Added:host::tests::hold_spawns;download::github::tests::a_missing_curl_reports_the_program_and_query_context(the error of acurlthat does not run, which the changed spawn produces); andsession::tests::endpoints::launched_waits_end_once_their_process_exitson Linux and Windows, with itsSleeperhelper andcheck_launched_waits_end_once_their_process_exits.Case matrix. No row of
doc/acceptance-case-matrix.mdchanges. The changed and added tests are unit tests ofnvx, outside its inventory of adapter tests, CI steps, doctests and microVM scenarios. No row names them, their files or this slice as coverage. This PR renames, moves or deletes no test, and its files hold no doctest.