Repository navigation
nvx-rs: move host::instance onto the store and supervisor - #721
Merged
Enrique Saurez (esaurez) merged 3 commits intoOct 23, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
The cross-platform lock and runtime-format switch affects VM recovery and termination and warrants final human review with full backend CI confirmation.
No open issues 🎉
What changed in this PR
Moves host::instance and its aci_edge_sandboxes consumer onto the shared store, supervisor, and session layers, continuing #620 after #675.
Changes:
- Unifies instance locking, launch recovery, reattachment, and shutdown; removes obsolete process helpers.
- Adds metadata-backed runtime identities and refuses incompatible runtime records.
- Updates documentation and tests for lifecycle recovery, identity mismatches, and stale-state cleanup.
| File | Description |
|---|---|
| scripts/test_nvx_tools.py | Updates harness expectations for artifact discovery and runtime records. |
| scripts/nvx_tools/aci_edge_sandboxes_tests.py | Passes the artifact directory and reads nested process IDs. |
| nvx-rs/tests/supervisor.rs | Removes checks for deleted process helpers. |
| nvx-rs/tests/store.rs | Uses supervisor spawning and verified process termination. |
| nvx-rs/tests/fake_openvmm.rs | Tests reattachment, identity mismatches, and forced shutdown. |
| nvx-rs/src/lib.rs | Updates the public lifecycle example. |
| nvx-rs/src/host/store/mod.rs | Updates imports and record-format documentation. |
| nvx-rs/src/host/process.rs | Removes obsolete lifecycle helpers. |
| nvx-rs/src/host/mod.rs | Documents shared instance supervision. |
| nvx-rs/src/host/instance.rs | Delegates lifecycle operations to shared host layers. |
| nvx-rs/src/host/artifacts.rs | Retains artifact origins for metadata-backed identities. |
| nvx-rs/README.md | Documents the revised APIs and example. |
| aci_edge_sandboxes/tests/support/mod.rs | Models interrupted launches using store journals. |
| aci_edge_sandboxes/tests/openvmm_unavailable.rs | Tests missing runtime identities for explicit paths. |
| aci_edge_sandboxes/tests/openvmm_fake.rs | Tests identity checks, recovery, and format rejection. |
| aci_edge_sandboxes/tests/openvmm_e2e.rs | Locates artifacts through their installation directory. |
| aci_edge_sandboxes/src/openvmm/state.rs | Adopts store locks and stale-directory cleanup. |
| aci_edge_sandboxes/src/openvmm/mod.rs | Routes backend operations through locked instances. |
| aci_edge_sandboxes/src/openvmm/config.rs | Adds explicit or metadata-derived runtime identities. |
| aci_edge_sandboxes/README.md | Documents identity requirements and lifecycle behavior. |
Review effort: Balanced
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-delegation-20261020
branch
from
October 22, 2026 00:35
773878f to
d219390
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-instance-20261021
branch
from
October 22, 2026 00:54
3ffb38d to
89f8230
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-delegation-20261020
branch
from
October 22, 2026 04:59
d219390 to
141a04b
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-instance-20261021
branch
from
October 22, 2026 05:55
89f8230 to
8b6729d
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-instance-20261021
branch
from
October 22, 2026 06:50
8b6729d to
a73868d
Compare
This was referenced Oct 22, 2026
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-instance-20261021
branch
from
October 22, 2026 21:07
a73868d to
55a3e8f
Compare
Enrique Saurez (esaurez)
changed the base branch from
esaurez/a1-host-h4-delegation-20261020
to
esaurez/a1-host-h4-switch-20261022
October 22, 2026 21:07
This was referenced Oct 22, 2026
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-switch-20261022
branch
from
October 22, 2026 21:44
d0553aa to
9951076
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-instance-20261021
branch
from
October 22, 2026 21:44
55a3e8f to
d73f87d
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-switch-20261022
branch
from
October 22, 2026 22:11
9951076 to
0e458e5
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-instance-20261021
branch
from
October 22, 2026 22:19
d73f87d to
3e5a32d
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-instance-20261021
branch
from
October 22, 2026 22:47
3e5a32d to
445bbe6
Compare
Artifacts that a release or a repository checkout holds now keep its ArtifactDir, and Artifacts::runtime_identity reads their runtime identity from its install metadata, through ArtifactDir::runtime_identity, without reading or hashing an artifact. Artifacts named by path have no identity, and neither do located artifacts whose paths were changed to other files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa7b9fda-3774-4817-aa98-af05f8d50215
host::instance kept its own runtime record, launch record, lock, and recovery. An instance is now a sandbox store of its own, which the store, the supervisor, and the control session drive, as the CLI's sandboxes are: - Instance::create and Instance::open name the store, and Instance::lock takes its lock and returns a LockedInstance. - LockedInstance::find reattaches to the recorded VM: it ends a start whose caller died, forgets a VM that exited, and reports a VM of another runtime identity as Mismatched without driving it. It asks for the caller's runtime identity only if the store records a VM or a launch. - LockedInstance::start launches OpenVMM through supervisor::launch under the caller's runtime identity, waits until the guest is ready, and commits the launch, or ends OpenVMM and abandons it. - LockedInstance::stop stops the VM, with a forced step after its timeout, or at once when no control session can reach the guest, as when the VM's capability cannot be read; exec then fails. - instance::check_launch checks that a launch fits OpenVMM's command line before anything is recorded. aci_edge_sandboxes keeps each sandbox's configuration in the sandbox's store, whose lock replaces the state root's .locks directory. The runtime identity comes from the install metadata of the configured artifacts, or from the new OpenVmmConfig::runtime_identity for artifacts named by path; without one, the backend is unavailable. A VM of another runtime identity fails every operation with backend_error and keeps running. sandbox.json keeps its format 3. The real-hypervisor test locates its artifacts in the checkout, whose build provenance identifies them. Runtime records and launch journals of instance's format 3 fail as unsupported, naming their format: stop running sandboxes with the build that started them before switching. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa7b9fda-3774-4817-aa98-af05f8d50215
host::instance launches, waits for, and ends OpenVMM through the supervisor now, so nothing uses host::process's spawn, detach_child, kill_child, wait_for_exit, kill, open_identity, and open_identity_with, or its re-export of supervisor::Launched. The store and supervisor tests use Process::open and the supervisor instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa7b9fda-3774-4817-aa98-af05f8d50215
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-host-h4-instance-20261021
branch
from
October 22, 2026 23:51
445bbe6 to
a869a3e
Compare
Enrique Saurez (esaurez)
marked this pull request as ready for review
October 22, 2026 23:53
Enrique Saurez (esaurez)
deleted the
esaurez/a1-host-h4-instance-20261021
branch
October 23, 2026 02:38
This was referenced Oct 23, 2026
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.

host::instance, whichaci_edge_sandboxesdrives, kept its own runtime record, launch record, lock, and spawn, wait, and kill code. This PR moves it onto the host layers that the CLI uses since #772:host::store,host::supervisor, andhost::session, under a runtime identity that the install metadata of the artifacts names. It then removes thehost::processhelpers that only the old code used.What changes
Instance. An
Instanceis a sandbox store in a directory of its own.Instance::createandInstance::openreplaceInstance::new, andInstance::locktakes the store's lock,sandbox.lock, and returns aLockedInstance:findreattaches to the recorded VM withsupervisor::reattach, as an orchestrator does after it restarts within the same boot: it ends a start whose caller died, forgets a VM that exited, and returns a live VM asFound::Running, which holds its capability, or the error of reading it, and a VM of another runtime identity asFound::Mismatched, which names the parts that differ and is never driven. It asks for the caller's runtime identity only if the store records a VM or a launch.startlaunches OpenVMM throughsupervisor::launchwith a fresh capability, under the caller'sRuntimeIdentity, waits until the guest answers with the required features and has mounted the mapped paths, and commits the launch; a failed start ends OpenVMM and abandons the launch.stopasks the guest to shut down through the session, and takes the supervisor's forced step after the timeout, as before, or at once when no session can reach the guest, as when the VM's capability cannot be read. Only control sessions need the capability: without it,execfails as reading it failed, and the VM still stops.Instance::execruns a workload of aRunningVM without the lock, as before.instance::check_launchchecks that a launch fits OpenVMM's command line before anything is recorded.Runtime identity.
Artifactslocated in a release or a checkout keep itsArtifactDir, andArtifacts::runtime_identityreads their identity from its install metadata with nvx-rs: prepare the CLI's move onto the store and supervisor #675's reader: a release'sSHA256SUMS, or a checkout's build provenance. Nothing hashes an artifact. Artifacts named by path have no identity, and neither do located artifacts whose paths were changed.aci_edge_sandboxes. Each sandbox's state directory is its instance's store, which holds the sandbox'ssandbox.jsonbeside the store's files.sandbox.lockreplaces the state root's.locks/directory. A call with a stale ID still leaves nothing behind.OpenVmmConfig::runtime_identity, which artifacts named by path (OpenVmmConfig::new, orNVX_OPENVMM,NVX_KERNEL, andNVX_INITRD) need. Without one,probe,provision, andstartfail withbackend_unavailable.LockedInstance::find. A VM of another runtime identity fails every operation withbackend_errorand keeps running; stopping it is left to the policy of nvx-rs: host cold integration for image-backed sandboxes #622.Removed:
instance'sSTATE_FORMAT,RuntimeRecord,LaunchRecord,ProcessIdentity, lock,create_private_dir,reconcile, and launch recovery; andhost::process::{spawn, detach_child, kill_child, wait_for_exit, kill, open_identity, open_identity_with}and its re-export ofsupervisor::Launched.Format switch
Switching to this build needs one stop of every running sandbox of
aci_edge_sandboxes, with the build that started it. Nothing is migrated: the store refuses the runtime records and launch journals ofinstance's format 3, in every operation, with a message that names the format and asks to stop the sandbox with the build that started it.sandbox.jsonkeeps its format 3, so stopped sandboxes start with this build. The earlier build's lock files stay in<state_root>/.locks/, which this build ignores; delete that directory once no earlier build runs. The two builds take different locks, so don't run them on one state root at once.Tests
nvx-rs/tests/fake_openvmm.rs, which drivesInstanceend to end against the fake OpenVMM:Mismatchedunder another OpenVMM identity, naming that part, and is still driven, and stopped, under its own;execfails as reading the capability fails, andstopterminates the VM and clears its records.host::instanceunit tests: an instance that records no VM needs no runtime identity; a runtime record or launch journal of format 3 fails, naming its format; a launch that does not fit the command line fails before the instance exists.host::artifacts: located artifacts have the identity of their directory, and lose it when their paths change.aci_edge_sandboxes:tests/openvmm_fake.rs: the runtime identity of a release comes from itsSHA256SUMS, a changed digest failsstart,exec,stop, anddeprovisionwithbackend_errorand leaves the VM running until the digest returns, and a missingSHA256SUMSfailsprobewithbackend_unavailable; runtime records of the previous format are refused; interrupted starts are recovered through the store's journal, before and after OpenVMM's identity is recorded, and a launch whose log is released ends; a sandbox whose capability file is lost failsexecwithbackend_error, still reportsalready_startedtostartanddeprovision, and stops withforced.tests/openvmm_unavailable.rs: artifacts named by path without a runtime identity failprobeandprovisionwithbackend_unavailable.src/openvmm/state.rs: stale IDs, also of an emptied directory or one holding only its lock file, and state removed while a caller waits for the lock, leave nothing behind.Part of #620. Refs #535.
Validation
Run on Windows 11 (MSVC, WHP) and on an Azure Linux 3 host with MSHV; the earlier heads below also ran on Ubuntu in WSL2 (KVM). Neither WSL's nested KVM nor the MSHV host can present the built-in profile of its CPU (
intel.alderlake.v1andamd.genoa.v1), so OpenVMM ran there through a wrapper that adds--cpu-profile host, whichcpu-profile-fallbackcannot pass by design.a869a3e8), ondevatd4d00af8, which merged nvx-rs: move the CLI's sandbox lifecycle onto the store and supervisor #772. Real VMs ran withnvx-rsbuilt from this branch and the artifacts thatnvx.py downloadinstalls from the nearest development release ofdev,v0.1.0-dev.3bd0c15ea785on WHP andv0.1.0-dev.4e0689fa9b10on MSHV, whose OpenVMM is7ff31ed7, the gitlink ofdev.python scripts/nvx.py verify.nvx-rs, withNVX_BUNDLE=skip, on Windows and Linux:cargo fmt --all --check;cargo clippy --locked --all-targets --all-features -- -D warnings;cargo clippy --locked --lib --no-default-features -- -D warnings;cargo test --locked --all-features, thencargo test --locked;cargo doc --locked --no-deps --all-featureswithRUSTDOCFLAGS=-D warnings. On Linux, the other steps ofcheck-nvx-rs: the 1.89 check, the macOS check, the artifact-bundling test, and the three configurations oftests/runtime-adapter.aci_edge_sandboxes: the steps ofcheck-aci-edge-sandboxeson Linux (fmt, clippy, tests,nvx.py test-agent-bundling, docs, the 1.89 check, the macOS check), and its fmt, clippy, tests, and docs on Windows.cargo check --locked --all-targets --all-featuresinnvx-rsandaci_edge_sandboxes, on Linux.shellcheckandshfmton the scripts thatcheck-qualitylists, and the tests ofvalidate-nvx,test_setup_azure.pyincluded.test_sleeper_subshell_records_and_reaps_its_sleeperspasses once no other run on the shared host has itssleep 611.25processes alive, because it looks for them in every process.check-qualitywith the pinned tools ofrequirements-dev.txt(ruff check,ruff format --check,pyrightfor Linux and Windows, the Markdown link check), the PowerShell syntax check, the tests ofvalidate-nvx, and thenvx.py --helpsmoke commands. Four process-containment tests oftest_adversarial.pyfail identically ondevwithout this PR.python scripts/nvx.py test-microvm --scenario managed-lifecycle --scenario sandbox-lifecycle --scenario managed-exec-config, andpython scripts/nvx.py test-aci-edge-sandboxes(18 tests).sudofor/dev/mshv: the same three scenarios; andtest-aci-edge-sandboxes, where 13 of 18 tests pass and the 5 that write through read-write mappings fail as they do without this PR, atbe28d631, because the unprivileged workload cannot write the root-owned directories that a root test creates.445bbe64, the same patches on nvx-rs: move the CLI's sandbox lifecycle onto the store and supervisor #772'sbe28d631, ondevat72e8ea26: the same gates, but for the Python steps on Windows, and the same real-VM runs, with the artifacts ofv0.1.0-dev.72e8ea2677bd.3e5a32dc, the same patches on nvx-rs: move the CLI's sandbox lifecycle onto the store and supervisor #772's0e458e5e, before nvx-rs: move the CLI's sandbox lifecycle onto the store and supervisor #772's last commit: the same gates and real-VM runs, with the artifacts ofv0.1.0-dev.bdaed9c6a7caon WHP and ofv0.1.0-dev.8483e3757469on MSHV. On Windows,test_adversarial.py'stest_close_is_bounded_when_a_surviving_child_holds_executor_stdoutalso failed in some runs, as it does ondev, because the removal of its temporary directory races the exit of the child that it killed.d73f87da, before the rebase, on nvx-rs: move the CLI's sandbox lifecycle onto the store and supervisor #772's9951076c, on nvx-rs: prepare the CLI's move onto the store and supervisor #675'sf5cff887, ondevated6a603f, with the same changes but for the rustdoc ofLockedInstance::start, which now takes nvx-rs: refuse read-only paths inside read-write directories on every host #672's wording: the same gates, and the same real-VM runs on WHP and MSHV, with the artifacts ofv0.1.0-dev.ed6a603f1e53. On WHP,test-aci-edge-sandboxespassed its 18 tests.a73868d9, the same patches on nvx-rs: prepare the CLI's move onto the store and supervisor #675's141a04b4, before nvx-rs: prepare the CLI's move onto the store and supervisor #675 was split into nvx-rs: prepare the CLI's move onto the store and supervisor #675 and nvx-rs: move the CLI's sandbox lifecycle onto the store and supervisor #772, ondevate714acbd: the same gates and real-VM runs, with the artifacts ofv0.1.0-dev.e714acbd47bfon WHP, KVM, and MSHV.89f82304, the same patches on nvx-rs: prepare the CLI's move onto the store and supervisor #675'sd2193903, ondevat4f9e7cbd, before nvx-rs: plan sandbox launches with the image format and upper #676: the same gates and real-VM runs, with the artifacts ofv0.1.0-dev.610a5ca1ca71on WHP, ofv0.1.0-dev.c61080e23971on KVM, and ofv0.1.0-dev.8f2565ba41ddon MSHV. At3ffb38df, on nvx-rs: prepare the CLI's move onto the store and supervisor #675's773878fb, ondevatc723fed1, before nvx-rs: separate distro verification from launch planning #653: the same, with the artifacts ofv0.1.0-dev.c39666f78111on WHP and KVM and ofv0.1.0-dev.c723fed17bd1on MSHV.7ece0b56, the same patches on nvx-rs: prepare the CLI's move onto the store and supervisor #675'sbea29f13, with the artifacts ofv0.1.0-dev.dad3e41d7f05:test-microvm --backend whpset.managed-lifecyclefailed twice in four runs because its guest console port was taken between the harness choosing it and OpenVMM binding it, which holding the port reproduces; the next runs passed.test-microvm --backend kvmset butcpu-profile-fallback.snapshot-corefailed its clock check twice right after WSL restarted, while WSL stepped its clock, and passed once it settled.Left to CI: the full matrix on KVM, MSHV, and WHP.