Repository navigation
Ship nvx-rs in NVX releases - #598
Merged
Merged
Conversation
Pedro Henrique Penna (ppenna)
requested review from
Enrique Saurez (esaurez)
and
a balanced review from Copilot
October 17, 2026 11:36
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Executable path consistency and thread-aware process liveness need correction before approval.
2 open issues
What changed in this PR
Ships nvx-rs with NVX releases, completing the release-delivery portion of #359 so compatible downloads can run delegated commands without Cargo.
Changes:
- Adds executable provenance, packaging, installation, and publication checks.
- Passes
nvx-rsartifacts through CI and validates them in adversarial execution. - Includes the stacked Rust CLI, guest-build migration, tests, and documentation updates.
| File | Description |
|---|---|
| SOURCE-MANIFEST.json | Records Rust guest toolchain and sources. |
| scripts/test_tool_versions.py | Checks musl target setup. |
| scripts/test_guest_binaries.py | Tests source identities and staged builds. |
| scripts/test_development_release.py | Tests required release executables. |
| scripts/test_adversarial.py | Tests executable discovery and metadata. |
| scripts/setup/setup-linux-runner.sh | Explicitly selects the target toolchain. |
| scripts/setup/setup-linux-mshv.sh | Installs and verifies musl support. |
| scripts/publish_development_release.py | Checks packages before publication. |
| scripts/nvx_tools/ubuntu.py | Normalizes and orders build inputs. |
| scripts/nvx_tools/sandbox_lifecycle_tests.py | Updates lifecycle and teardown checks. |
| scripts/nvx_tools/microvm_test_scripts/sandbox-blocks.sh | Tests distro-only block layout. |
| scripts/nvx_tools/guest_binaries.py | Validates staged Rust guest binaries. |
| scripts/nvx_tools/ci.py | Tracks Rust guest build dependencies. |
| scripts/nvx_tools/build.py | Installs shared Rust guest binaries. |
| scripts/nvx_tools/build_constants.py | Defines Rust build and source packaging inputs. |
| scripts/nvx_tools/benchmark.py | Records Rust guest provenance. |
| scripts/nvx_tools/azurelinux.py | Includes normalized Rust build inputs. |
| scripts/nvx_tools/archive.py | Excludes generated Rust build output. |
| scripts/nvx_tools/adversarial_executor.py | Validates and records nvx-rs. |
| README.md | Explains prebuilt CLI availability. |
| nvx-rs/src/protocol/mod.rs | Updates shared-protocol documentation. |
| nvx-rs/src/protocol/features.rs | Defines layered sandbox requirements. |
| nvx-rs/src/host/sys/mod.rs | Exposes interrupt handling. |
| nvx-rs/src/host/sys/interrupt.rs | Records Unix interrupts. |
| nvx-rs/src/host/session.rs | Exposes CLI request and response methods. |
| nvx-rs/src/host/sandbox/layer.rs | Defines distro-layer parsing and validation. |
| nvx-rs/src/host/python/uuid.rs | Implements Python-compatible UUID parsing. |
| nvx-rs/src/host/python/shell.rs | Implements command display quoting. |
| nvx-rs/src/host/python/mod.rs | Organizes Python compatibility helpers. |
| nvx-rs/src/host/python/dumps.rs | Implements compatible JSON formatting. |
| nvx-rs/src/host/process.rs | Adds CLI process-control methods. |
| nvx-rs/src/host/platform/windows.rs | Adds Windows CLI primitives. |
| nvx-rs/src/host/platform/other.rs | Provides interrupt compatibility hooks. |
| nvx-rs/src/host/platform/linux.rs | Exposes Linux interrupt hooks. |
| nvx-rs/src/host/mod.rs | Exposes sandbox and CLI modules. |
| nvx-rs/src/host/cli/sandbox/identity.rs | Checks sandbox process identity. |
| nvx-rs/src/host/cli/output.rs | Implements CLI stream output. |
| nvx-rs/src/host/cli/egress.rs | Adds egress compilation commands. |
| nvx-rs/src/host/cli/deadline.rs | Adds interruptible deadline handling. |
| nvx-rs/src/host/cli/artifacts.rs | Locates and reports runtime artifacts. |
| nvx-rs/src/guest/time/mod.rs | Updates guest-time documentation. |
| nvx-rs/src/guest/time_probe/mod.rs | Updates probe documentation. |
| nvx-rs/src/guest/reseed.rs | Removes obsolete C references in comments. |
| nvx-rs/src/bin/nvx-rs.rs | Adds the CLI entry point. |
| nvx-rs/Cargo.toml | Declares CLI features and dependencies. |
| guest/common/nvx-reseed.c | Removes the replaced C implementation. |
| guest/common/nvx-mmio-write.c | Removes the replaced C implementation. |
| guest/common/nvx-init-agent | Restricts layers and reserves runtime paths. |
| guest/common/nvx-exit.c | Removes the replaced C implementation. |
| guest/common/nvx-console-pending.c | Removes the replaced C implementation. |
| docker/Dockerfile | Shares Rust binaries across guest builds. |
| doc/setup.md | Documents Rust build prerequisites. |
| doc/run.md | Documents delegated sandbox execution. |
| doc/project-structure.md | Describes the Rust-backed layout. |
| doc/guests/ubuntu-guest.md | Documents Rust guest binaries. |
| doc/guests/azurelinux-guest.md | Documents guest binary provenance. |
| doc/distribution.md | Documents CLI packaging and installation. |
| doc/contribute.md | Clarifies Rust ownership and testing. |
| doc/ci.md | Documents artifact handoff and release checks. |
| doc/build.md | Documents build provenance and package layout. |
| doc/benchmarks.md | Documents Rust benchmark provenance. |
| doc/adversarial-testing.md | Documents target CLI requirements. |
| aci_edge_sandboxes/src/openvmm/mod.rs | Updates shared egress documentation and tests. |
| aci_edge_sandboxes/README.md | Documents egress behavior changes. |
| .github/workflows/run-platform.yml | Packages on PRs without publishing. |
| .github/workflows/run-nvx-microvm-tests.yml | Downloads the CLI for scenarios. |
| .github/workflows/copilot-setup-steps.yml | Tracks all Rust source changes. |
| .github/copilot-instructions.md | Updates build and runtime guidance. |
| .github/actions/validate-nvx/action.yml | Removes replaced Python test suites. |
| .github/actions/package-release/action.yml | Downloads CLI artifacts and gates uploads. |
| .github/actions/check-nvx-rs/action.yml | Updates the synthetic release layout. |
| .github/actions/build-guest-artifacts/action.yml | Keys caches on Rust source identity. |
| .gitattributes | Marks frozen compatibility fixtures generated. |
Review effort: Balanced
Pedro Henrique Penna (ppenna)
added a commit
that referenced
this pull request
Oct 17, 2026
nvx-rs sandbox identifies the OpenVMM of a running sandbox by its process ID and start time, which identity.rs reads on Linux from /proc/PID/stat. Ported from the former sandbox_lifecycle.py, it counted every zombie as exited. But /proc reports a process as a zombie as soon as its main thread exits, while its other threads may still run and hold its descriptors, so exec and stop could reject a live VM as stale, and deprovision could remove the state of a VM that still runs. The library's platform::process_start_time, with which the process helpers wait for and kill OpenVMM, already counts such a process as running until its last thread exits, as Windows does, and so does the CLI's own Windows branch; only its Linux branch disagreed. linux_start_time now asks, for a zombie only, whether other threads remain, through the platform's other_threads_remain, which reads /proc/PID/task, and otherwise keeps its Python-compatible parsing and messages. other_threads_remain becomes pub(crate) for that. The stat-line test covers a zombie with and without other threads, and an error while listing them. A new Linux test, like process::tests::a_process_runs_until_its_last_thread_exits, makes the main thread of a Python process exit through pthread_exit while another thread sleeps: process_running holds until the process is killed. It fails without the fix. Copilot's review of #598 flagged this in code that #596 added, the nvx-rs CLI; the platform helper is already on dev. The change stays within the CLI's Linux identity check. Part of #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
added a commit
that referenced
this pull request
Oct 17, 2026
The executor validates and hashes the nvx-rs that NVX_RS names before the baseline boot, and nvx.py resolves a relative NVX_RS against its working directory: the executor's, at that point. The scenarios inherit NVX_RS unchanged but run in the repository root, so with a relative NVX_RS they ran another executable than the one validated, or found none. sanitized_environment() now makes NVX_RS absolute against the executor's working directory, so that the scenarios run the executable that the initialization checked. An empty NVX_RS, which nvx.py ignores, passes unchanged. A new test validates a relative NVX_RS in one working directory and resolves it in a scenario process in another; it fails without the fix. The environment test now expects the absolute path. Copilot's review of #598 flagged this. #597 added NVX_RS to the scenarios' environment, and the earlier commit of this series that validates the executor's nvx-rs made the mismatch matter. Closes #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Oct 19, 2026
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-359-release
branch
from
October 19, 2026 19:14
ad43ce0 to
53a3e3e
Compare
Pedro Henrique Penna (ppenna)
added a commit
that referenced
this pull request
Oct 19, 2026
nvx-rs sandbox identifies the OpenVMM of a running sandbox by its process ID and start time, which identity.rs reads on Linux from /proc/PID/stat. Ported from the former sandbox_lifecycle.py, it counted every zombie as exited. But /proc reports a process as a zombie as soon as its main thread exits, while its other threads may still run and hold its descriptors, so exec and stop could reject a live VM as stale, and deprovision could remove the state of a VM that still runs. The library's platform::process_start_time, with which the process helpers wait for and kill OpenVMM, already counts such a process as running until its last thread exits, as Windows does, and so does the CLI's own Windows branch; only its Linux branch disagreed. linux_start_time now asks, for a zombie only, whether other threads remain, through the platform's other_threads_remain, which reads /proc/PID/task, and otherwise keeps its Python-compatible parsing and messages. other_threads_remain becomes pub(crate) for that. The stat-line test covers a zombie with and without other threads, and an error while listing them. A new Linux test, like process::tests::a_process_runs_until_its_last_thread_exits, makes the main thread of a Python process exit through pthread_exit while another thread sleeps: process_running holds until the process is killed. It fails without the fix. Copilot's review of #598 flagged this in code that #596 added, the nvx-rs CLI; the platform helper is already on dev. The change stays within the CLI's Linux identity check. Part of #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
added a commit
that referenced
this pull request
Oct 19, 2026
The executor validates and hashes the nvx-rs that NVX_RS names before the baseline boot, and nvx.py resolves a relative NVX_RS against its working directory: the executor's, at that point. The scenarios inherit NVX_RS unchanged but run in the repository root, so with a relative NVX_RS they ran another executable than the one validated, or found none. sanitized_environment() now makes NVX_RS absolute against the executor's working directory, so that the scenarios run the executable that the initialization checked. An empty NVX_RS, which nvx.py ignores, passes unchanged. A new test validates a relative NVX_RS in one working directory and resolves it in a scenario process in another; it fails without the fix. The environment test now expects the absolute path. Copilot's review of #598 flagged this. #597 added NVX_RS to the scenarios' environment, and the earlier commit of this series that validates the executor's nvx-rs made the mismatch matter. Closes #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nvx.py runs the nvx-rs that NVX_RS names or else the one in nvx-rs/target/release, and never rebuilds one that exists, so nothing tells whether that executable is a build of the checkout's nvx-rs sources. OpenVMM has the same problem and solves it with build/openvmm.provenance.json, which binds the digest of its binary to its gitlink revision. Releases are about to ship nvx-rs, and packaging and `nvx.py download` need the same binding for it. build/nvx-rs.provenance.json now records, for the nvx-rs that nvx.py runs: - source_sha256: a digest of the files that build nvx-rs, the crate's Cargo.toml, Cargo.lock, build.rs, and src/ tree, and rust-toolchain.toml, by path and with line endings normalized, so that LF and CRLF checkouts of the same sources agree. The tests, README.md, and artifacts.json build nothing, and stay out; - toolchain: the Rust release that rust-toolchain.toml pins; - target: the Rust target of the build; - features: ["cli"], which BUILD_COMMAND builds; - executable_sha256: the digest of the executable. nvx.py records it after each Cargo build that it makes, for the host target that `rustc -vV` reports. The new `nvx.py record-nvx-rs-provenance [--target TARGET]` command records it for a build made otherwise, of the executable that NVX_RS names or else of nvx-rs/target/release/nvx-rs[.exe]. The caller vouches that this checkout built that executable for that target; the record lets later steps check the claim against the sources. RELEASE_TARGETS names the targets that the NVX CLI jobs build for releases, x86_64-unknown-linux-musl and x86_64-pc-windows-msvc, and a record for the musl target requires a static ELF executable, since a build for the host's own Linux target is dynamic. The new constants live in nvx_tools/nvx_rs.py, which no guest artifact digests, so the existing guest provenance stays valid. doc/usage.md documents the command and the record, doc/build.md the file beside the other provenance sidecars, nvx-rs/README.md who records it, and doc/ci.md that the Copilot setup's first nvx-rs build records it too. Part of #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#359 makes nvx-rs the backend of nvx.py sandbox and of the egress and artifact steps of nvx.py run, so a release that nvx.py download installs is of little use without it. Every package now ships the release build of nvx-rs beside OpenVMM: - bin/nvx-rs in the Linux packages, the static x86_64-unknown-linux-musl executable that runs on every x86-64 Linux host, and bin/nvx-rs.exe in the Windows package, the x86_64-pc-windows-msvc executable with a static C runtime; - provenance/nvx-rs.provenance.json, its build provenance; - an nvx_rs section in SOURCE-MANIFEST.json with the executable's digest, the source digest, the Rust release, the target, and the features; - both files in SHA256SUMS, as every packaged file is. `nvx.py package` takes them from nvx-rs/target/release and build/, as it takes OpenVMM, and refuses a provenance that does not describe the executable as a build of the checkout's nvx-rs sources, with its Rust release and features, for the release target of the host's OS, and a Linux executable that is not static. It checks the staged copies against the provenance, as it does for OpenVMM. Source-inclusive packages ship nvx-rs too; their project source archive already holds its sources. CI provides the build. Each NVX CLI job now records the provenance of the nvx-rs that it built and tested, with --target, and uploads it as nvx-rs-linux-musl-provenance or nvx-rs-windows-msvc-provenance beside the executable. The workflows now name the pair by its base name, as they name the OpenVMM artifacts, so run-nvx-microvm-tests.yml appends -executable itself. run-platform.yml takes the base name and passes it to the package-release action, which downloads both into place before it packages. The platform jobs already wait for their CLI job. Before it publishes, publish_development_release.py checks, from each archive's member list, that every platform's package ships bin/openvmm and bin/nvx-rs, or their .exe forms, so that a development release without them never goes out. The bundled feature of nvx-rs extracts and checks only OpenVMM, the Alpine guest, and SOURCE-MANIFEST.json of a release, so a release with bin/nvx-rs stages as the pinned older release does, and the pin stays. The synthetic release of the check-nvx-rs action now has the current layout, with nvx-rs and its provenance in the bundle and in its SHA256SUMS. doc/distribution.md, doc/build.md, doc/usage.md, doc/ci.md, and nvx-rs/README.md describe the package contents and the handoff. Part of #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Packages now ship bin/nvx-rs[.exe], which statically links the Rust standard library, the crates of the nvx-rs cli feature, and a C runtime, so THIRD_PARTY_NOTICES.md, which every package ships, must cover them, as it has covered the Rust guest binaries since #561. The crate rows follow `cargo tree --locked --features cli -e normal` in nvx-rs for both release targets, x86_64-unknown-linux-musl and x86_64-pc-windows-msvc: 26 crates, with the licenses that `cargo metadata` reports. - The rows of the crates that the guest binaries already link, the Rust standard library, compiler_builtins, libc, nix, getrandom, bitflags, cfg-if, and memoffset, now name bin/nvx-rs too. - New rows cover serde, serde_core, serde_json and its itoa, memchr, and zmij, sha2 and its RustCrypto dependencies, and windows-sys and windows-link on Windows. serde_derive and the proc-macro2, quote, syn, and unicode-ident crates that it runs on are marked as build time only, not linked. - The musl row now includes the bin/nvx-rs of the linux-kvm and linux-mshv packages, under the musl terms that it already documents. - A new LLVM libunwind row covers the unwinder that the Rust musl target links into the guest binaries and the Linux nvx-rs, under Apache-2.0 WITH LLVM-exception, which lifts the attribution conditions for compiled code. - A new Microsoft C runtime row covers the static CRT of bin/nvx-rs.exe, under the Visual Studio redistribution terms. doc/distribution.md says the same in its licensing section. A test parses nvx-rs/Cargo.lock, requires each of its crates to be classified as shipped or not, and checks that every shipped crate has a row with its exact version that names bin/nvx-rs, and that the runtime rows name it too. Part of #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The platform jobs packaged a release only on dev pushes, so a pull request that broke packaging, as one that changes what a package ships can, passed CI and failed the next development release instead. The Windows packaging failure that the first commit of this series fixes had gone unnoticed that way. The Package release step of run-platform.yml now runs whenever the platform packages releases, and tells the package-release action through a new upload input whether to upload the archive: true only on dev pushes. The action stages the package, which checks every input against its provenance, and archives it, which checks the staged files against SHA256SUMS, on every run, and uploads the archive for the development release only when asked. Pull requests still publish nothing; the release job runs only on dev pushes. Packaging adds about a minute and a half to each Linux platform job and twenty seconds to the Windows one. doc/distribution.md describes it, and a test checks the conditions. Part of #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nvx.py download installed OpenVMM and the guest artifacts of a release, so nvx.py sandbox and nvx.py run still needed Cargo to build nvx-rs afterwards. It now installs the package's bin/nvx-rs[.exe] at nvx-rs/target/release/nvx-rs[.exe], where nvx.py finds it, and its provenance at build/nvx-rs.provenance.json, so that both commands run without Cargo after a download. nvx.py runs that executable without ever rebuilding it, and download may fall back to the release of an earlier first-parent commit, so it installs nvx-rs only when the package's provenance describes the build of this checkout's nvx-rs sources that nvx.py runs: the same source digest, Rust release, and features, for the release target of this OS. Otherwise, as for a release from before packages shipped nvx-rs, it installs everything else, removes any earlier nvx-rs and its provenance, which may not match the checkout either, and says that nvx.py builds nvx-rs with Cargo when a command needs it, unless NVX_RS names one. Older releases thus stay installable. A package whose nvx-rs does not match its own provenance is broken, and fails the download before it replaces anything. The installed OpenVMM and nvx-rs are now executable on POSIX whatever mode the archive records. The message that nvx.py prints without Cargo, the discovery notes in nvx_rs.py, doc/usage.md, doc/setup.md, doc/distribution.md, README.md, and nvx-rs/README.md describe the installed nvx-rs. Part of #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The adversarial executor validates and hashes OpenVMM, the kernel, the initramfs, and their provenance before the baseline boot, so that a campaign runs what the staged commit built. Scenarios such as l3-l4-egress-policy also run nvx-rs through nvx.py, which never rebuilds an nvx-rs that exists, so one left from before a change of the nvx-rs sources ran unchecked. validate_runtime_artifact_provenance() now also requires the nvx-rs that nvx.py runs, the one that NVX_RS names or else nvx-rs/target/release/nvx-rs[.exe], with a build/nvx-rs.provenance.json that describes it as a build of the checkout's nvx-rs sources, for any target. The executor's artifact metadata hashes both, beside the other artifacts. A stale, unrecorded, or missing nvx-rs now fails the initialization instead of running. Targets already had to build nvx-rs before exposure; the preparation in doc/adversarial-testing.md now also records its provenance, which nvx.py does itself for the builds that it makes, and a target with the release of the staged commit gets both from nvx.py download. doc/ci.md and doc/usage.md mention the check. Closes #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nvx-rs sandbox identifies the OpenVMM of a running sandbox by its process ID and start time, which identity.rs reads on Linux from /proc/PID/stat. Ported from the former sandbox_lifecycle.py, it counted every zombie as exited. But /proc reports a process as a zombie as soon as its main thread exits, while its other threads may still run and hold its descriptors, so exec and stop could reject a live VM as stale, and deprovision could remove the state of a VM that still runs. The library's platform::process_start_time, with which the process helpers wait for and kill OpenVMM, already counts such a process as running until its last thread exits, as Windows does, and so does the CLI's own Windows branch; only its Linux branch disagreed. linux_start_time now asks, for a zombie only, whether other threads remain, through the platform's other_threads_remain, which reads /proc/PID/task, and otherwise keeps its Python-compatible parsing and messages. other_threads_remain becomes pub(crate) for that. The stat-line test covers a zombie with and without other threads, and an error while listing them. A new Linux test, like process::tests::a_process_runs_until_its_last_thread_exits, makes the main thread of a Python process exit through pthread_exit while another thread sleeps: process_running holds until the process is killed. It fails without the fix. Copilot's review of #598 flagged this in code that #596 added, the nvx-rs CLI; the platform helper is already on dev. The change stays within the CLI's Linux identity check. Part of #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The executor validates and hashes the nvx-rs that NVX_RS names before the baseline boot, and nvx.py resolves a relative NVX_RS against its working directory: the executor's, at that point. The scenarios inherit NVX_RS unchanged but run in the repository root, so with a relative NVX_RS they ran another executable than the one validated, or found none. sanitized_environment() now makes NVX_RS absolute against the executor's working directory, so that the scenarios run the executable that the initialization checked. An empty NVX_RS, which nvx.py ignores, passes unchanged. A new test validates a relative NVX_RS in one working directory and resolves it in a scenario process in another; it fails without the fix. The environment test now expects the absolute path. Copilot's review of #598 flagged this. #597 added NVX_RS to the scenarios' environment, and the earlier commit of this series that validates the executor's nvx-rs made the mismatch matter. Closes #359. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-359-release
branch
from
October 19, 2026 19:36
53a3e3e to
fdeb959
Compare
Pedro Henrique Penna (ppenna)
deleted the
agents/fix-issue-359-release
branch
October 19, 2026 20:30
This was referenced Oct 20, 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.


Closes #359 (PR 5 of 5).
Summary
Every NVX release package now ships the
nvx-rsCLI to whichnvx.py sandboxand the egress and artifact steps ofnvx.py rundelegate since #597, andnvx.py downloadinstalls it wherenvx.pyfinds it, so a downloaded checkout runs both commands without Cargo. The executable travels with a build provenance, as OpenVMM's does, which packaging,download, and the adversarial executor check.build/nvx-rs.provenance.jsonbinds the executable's SHA-256 to a line-ending-independent digest of the files that buildnvx-rs(Cargo.toml,Cargo.lock,build.rs,src/, andrust-toolchain.toml), the pinned Rust release, the target, and theclifeature.nvx.pyrecords it after its own Cargo builds, and the newnvx.py record-nvx-rs-provenance [--target TARGET]records it for a build made otherwise.nvx.py packageshipsbin/nvx-rs[.exe]andprovenance/nvx-rs.provenance.json, lists both inSHA256SUMS, and records annvx_rssection inSOURCE-MANIFEST.json. It refuses annvx-rswhose provenance does not describe it as the build of the checkout's sources for the release target of the host's OS, and a Linuxnvx-rsthat is not static.nvx-rsthat they build and test, and upload it beside the executable. The platform jobs hand both topackage-release. The platform jobs now also package and archive on pull requests, without uploading, so this PR's platform jobs exercise packaging; before, onlydevpushes did. The development release job refuses packages withoutbin/openvmmandbin/nvx-rs.downloadverifiesSHA256SUMS, then installsbin/nvx-rs[.exe]atnvx-rs/target/release/nvx-rs[.exe], executable, with its provenance inbuild/, when the provenance describes the build of the checkout'snvx-rssources thatnvx.pyruns. Otherwise it installs the rest and leavesnvx-rsto Cargo.nvx-rsthat the scenarios run, so a stalenvx-rs/target/release/nvx-rsfails the initialization instead of running.THIRD_PARTY_NOTICES.mdcovers the crates, the musl, LLVMlibunwind, and Microsoft C runtime code that the shipped executables link.Package changes
linux-kvm,linux-mshvwindows-whpbin/nvx-rs[.exe](new)bin/nvx-rs: staticx86_64-unknown-linux-muslexecutable, mode 0755bin/nvx-rs.exe:x86_64-pc-windows-msvc, static C runtimeprovenance/nvx-rs.provenance.json(new)format,source_sha256,toolchain(1.95.0),target,features(["cli"]),executable_sha256SOURCE-MANIFEST.jsonnvx_rssection with the executable's SHA-256, the source digest, toolchain, target, and featuresSHA256SUMSChoices:
bin/nvx-rs, or whosenvx-rswas built from othernvx-rssources (a first-parent fallback release), still installs everything else;downloadthen removes any earliernvx-rsand its provenance, says why, andnvx.pybuildsnvx-rswith Cargo when a command needs it, as before this PR. A package whosenvx-rscontradicts its own provenance is broken and fails the download before anything is replaced.bundledpin stays. Thebundledbuild script extracts and checks only OpenVMM, the Alpine guest, and the manifest of a release, so a release withbin/nvx-rsstages as the pinned older one does. The synthetic release of thecheck-nvx-rsaction now has the current layout, and the pinned release still stages and tests.nvx-rstoo; their project source archive already holds its sources.Stack
Rebased onto latest
origin/devatcee5471f0256289e04ca2c01052f38afb7898669after #561, #594, #595, #596, and #597 merged. Only this PR's eight release-specific commits remain. The Ubuntu converter-order fix is already upstream and was dropped during the rebase.This PR's commits:
scripts: record the provenance of nvx-rsrelease: package nvx-rs with its provenancenotices: cover the nvx-rs binaryci: package releases on pull requestsrelease: install nvx-rs with nvx.py downloadadversarial: check the nvx-rs that the scenarios runnvx-rs: count a VM as running until its last thread exits(review)adversarial: hand the scenarios the nvx-rs that the executor checked(review)Commits 2, 6, and 8 extend the artifact handoff and adversarial executor from #597. Commit 7 fixes the Linux identity check introduced by #596. Fresh review corrections were folded into the owning original commits: commit 5 checks executable/provenance consistency before fallback compatibility, and commit 6 aligns the controller's nine-entry artifact evidence schema with the executor. Both corrections have regressions demonstrated failing before and passing after the fixes; their review threads are resolved.
#359 acceptance criteria
nvx.py sandbox …, and the egress and artifact steps ofnvx.py run, go throughnvx-rsnvx-rsransandbox runandrun's artifact and egress steps on KVM, MSHV, and WHP without Cargocontrol_session.py,egress_policy.py,sandbox_lifecycle.py, and the launch profile ofsandbox.py, with their tests ported to Rustnvx-rs, andnvx.py downloadinstalls itSHA256SUMS, manifest, and provenance (commit 3), the publication check (3), the installer (6), their tests intest_nvx_tools.pyandtest_development_release.py, and the hardware runs below. This PR's platform jobs package each platformmanaged-exec-config,sandbox-lifecycle, andmanaged-lifecycleon prometheus32, prometheus30, and prometheus28, and in CI's microVM jobs, which run again on this PRRebase validation (2026-10-19)
Rebased head:
fdeb9592765db3d2f5e9f7b57c475bfc8e26636c.fcntl; the suite passed on Linux instead.fdeb9592765db3d2f5e9f7b57c475bfc8e26636c. Linux/KVM, Linux/MSHV, Windows/WHP, platform packaging, OpenVMM tests, microVM tests, the performance regression gate, and the required status check all passed. Only intentionally skipped jobs remain; PR CI did not publish a development release or persist performance history.origin/dev(cee5471f0256289e04ca2c01052f38afb7898669).Original validation
nvx.py verify; nvx-rs: fmt, clippy (all features, and lib without defaults), tests (all features and default), rustdoc with-D warnings, 1.89 check,aarch64-apple-darwincheck, and the bundled-artifacts steps with the synthetic current-layout release and the pinned release; aci_edge_sandboxes: the commands of check-aci-edge-sandboxes andtest-agent-bundling; Ruff, Ruff format, Pyright (Linux and Windows), the Markdown link check, compileall, the validate-nvx unittest list (996 tests),test_adversarial.py(73),test_hosts.py, and the CLI help commandstest_nvx_tools.py,test_development_release.py,test_markdown_links.py, andtest_adversarial.pywhere it changestest_hosts.py, the Specula wiring tests, and actionlint 1.7.7 (only the pre-existingnvx-adversarial-controllerlabel note).test_adversarial.pypasses on 3.14 and 3.12, and on 3.10 fails onlytest_contained_native_process_preserves_child_signal_status(sigkill), as onorigin/devpackage --binary-onlyandarchive-releasewith #597's CI guest artifacts and OpenVMM, then an install withdownload's installer into a scratch clone:sandbox run --dry-runandrun --dry-runwith an egress policy file, without Cargo and withNVX_RSunsetnvx-rsbuilt forx86_64-unknown-linux-muslas CI does (static-pie), its provenance recorded, alinux-kvmbinary-only package with the host's OpenVMM at5ab8a84and #597's CI guest artifacts, archived (269 MB);sha256sum -c SHA256SUMSpasses; the extracted release'sbin/nvx-rs artifactsfinds the release layout; installed into a scratch checkout withdownload's installer, which verifiesSHA256SUMSand the provenance. Without Cargo and withNVX_RSunset:sandbox run --dry-runand a realsandbox runof/bin/echo(hello-from-a-release, outcome success, complete teardown), the egress policy file throughsandbox runandrun, whose dry run also finds the kernel and initramfs; nothing rebuiltnvx-rslinux-mshvpackage (268 MB): all passnvx-rs.exebuilt with a static C runtime as CI does, and awindows-whppackage (151 MB, 22SHA256SUMSentries, no mismatches): all pass-D warnings, 1.89 andaarch64-apple-darwinchecks; Ruff, Pyright,test_adversarial.py(74). WSL: nvx-rs fmt, clippy, all tests, including the new Linux testa_vm_runs_until_its_last_thread_exits, which fails without the fix, and the 1.89 check;test_adversarial.pyon 3.14 and 3.12 (74), and on 3.10 with only the known sigkill failure. The new executor test fails without its fixdevpushes