Repository navigation
host: hand prepared cold launches to external callers - #1349
Closed
Enrique Saurez (esaurez) wants to merge 42 commits into
Closed
Enrique Saurez (esaurez) wants to merge 42 commits into
Enrique Saurez (esaurez) wants to merge 42 commits into
Conversation
Add caller-owned VM directories, fixed output names and redacted control capabilities without creating launch outputs or persisting credentials. Require current-user ownership and mode 0700 on Linux, or a protected owner-only DACL on Windows, without changing existing directories. Use the crate's Result and report a non-private directory as ErrorKind::Io. Normalize trailing separators and dot components before no-follow checks so a final symbolic link is refused, while preserving parent components. Keep the existing store privacy policies unchanged. Compare Windows ACL snapshots using the same canonical SDDL SID spellings as the descriptor serializer, including well-known aliases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d334d041-d2f2-47db-be92-5564ff14f2d7
Add VmConfig in the new host::config: a VM's processors, its memory in MiB, its root, blockless or layered, the View that its workload has of it, the export plan of the host paths that it exports, and where its boot console's output goes, to a listener or nowhere; the console's device exists either way. A layered root names its image and format, an optional bound of a GPT disk's first partition, its overlay mode, and a scratch upper with its journal policy or a RAM upper whose cap is 1 to 90 percent of the VM's memory, 50 by default. VmConfig::check checks the whole value without reading the host, and returns the first refusal as an Error of kind Unsupported, whose message names what it refuses; an export's refusal keeps the error of ExportPlan::check as its source. It reuses activation's journal, overlay and machine-ID vocabulary, the record's hostname rule, which becomes activation::is_hostname for both, and ExportPlan::check with the guest targets that the root reserves. Add activation::View, the hostname and machine ID that a VM's workload sees, beside WorkloadView, which the record keeps for now. The public API snapshots list the new items. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 869204bf-28f8-408e-bcab-e4353944ef69 Copilot-Session: 2d81c32c-865b-42d0-8f3e-6881809e0157
The fixture already freezes socket tables and waits for stubbed server output. Observe a failed condition twice instead of spawning 101 awk or grep processes over immutable input. Keep all 13 cases, the 10-second subprocess timeout and production polling unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2d81c32c-865b-42d0-8f3e-6881809e0157
…pper The boot tokens of a layered root gain four optional settings, after the image's and the upper's: nvx_overlay_mode, trusted-lowers or untrusted-lowers; nvx_scratch_journal, allowed or forbidden; nvx_first_partition_bound, a GPT disk's bound in bytes; and nvx_ram_upper_percent, a RAM upper's cap from 1 to 90 percent of the VM's memory. RootLayout names each as an Option that renders no token when it is None, so that the guest keeps its default. RootLayout::parse reads them strictly: each once, with a value in its decimal or named form, only beside an image, and the journal policy only with a scratch upper, the cap with a RAM upper, and the bound with a GPT image. The shared fixture records the new cases, which nvx-rs and the shell init both replay. guest::root's Upper::Tmpfs takes the cap, mounts the tmpfs with size=N% when it has one, and refuses a cap outside 1 to 90 percent before anything changes, since the kernel reads 0 as no cap. NVX's shell init reads the four tokens with the parser's rules and messages, mounts a capped RAM upper with size=N%, and refuses, before it mounts anything, untrusted lowers and a forbidden journal, which it cannot assemble, as it refuses a GPT image. The sandbox plan names none of the settings, so its command lines stay as they were, and an adapter's plan refuses a setting that its command line names, since it reads the root's tokens back. A clone's activation assembles its tmpfs without a cap, since its record names none, and the runtime adapter's worker hands the cap of its boot tokens to its root. BOOT_CONTRACT_VERSION stays 5, since nothing deployed renders the new tokens. The public API snapshots list the new items. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
The executor-close fixture kills its surviving Windows child, whose stderr still names a file in its temporary directory. Wait for the process object to signal before cleaning up that directory, using the existing bounded Windows wait helper and the fixture's existing cleanup deadline. Keep the workload timeout, the measured close bound, and every existing test assertion unchanged. A child that does not exit now fails explicitly instead of racing the temporary-directory cleanup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Wait for the real fixture to publish its boot readiness and for its descendant to publish its PID before starting the existing half-second workload timeout. Keep that startup wait bounded and clean up the contained processes if readiness fails. Accept the runner's raw TimeoutError and its RuntimeError wrapper only with the expected half-second workload-timeout message. Require a live, positive descendant PID before the workload and retain the check that it has exited after the timeout. Production code, measured deadlines, and skips do not change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Apply the repository formatter's quoting and wrapping to the approved fixture correction. Its parsed Python syntax tree is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Add net::Ipv4Cidr, a static guest address checked when it is built: an IPv4 address with a /1 to /30 prefix that is not its network's own, broadcast, or gateway address, as OpenVMM accepts it. It displays and parses as ADDRESS/PREFIX, and with the serde feature it serializes as that text. is_valid_guest_network and guest_gateway read addresses through it, with the same rules. VmConfig gains its network, a NetworkConfig of the policy that OpenVMM enforces and the NIC's address, which a cold boot without one takes from DEFAULT_GUEST_NETWORK, and a restore from the snapshot; and its boot, a Boot of the caller's own kernel command-line tokens, which only a cold boot takes. NicConfig names the NIC that a VM settled on. DEFAULT_GUEST_NETWORK moves here from host::launch, which re-exports it. VmConfig::check adds no refusal: the policy and the tokens are checked where they are lowered. The configuration tests' fixture gains the two fields. The public API snapshots list the new items. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ab593c71-7f27-4207-8ff3-829d9a283b4c
Read the root settings before the init refuses GPT images, so an invalid unsigned bound has the same numeric diagnostic in either token order. Keep GPT unsupported and pin the existing image-first overflow fixture's shell diagnostic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Reconcile configuration and root-token inputs without additional implementation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c390c333-e511-4ec5-a133-8352cb86d485
Join the unchanged pure SL2.4a2 inputs with the source-equivalent published SL2.3b squash so that the candidate retains the actual feature ancestry. The input tree is byte-identical; this adds no owned implementation. Technical boundary: ENG-24a2-INPUT-ANCESTRY-8ED-SAME-TREE. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c390c333-e511-4ec5-a133-8352cb86d485
…pper The boot tokens of a layered root gain four optional settings, after the image's and the upper's: nvx_overlay_mode, trusted-lowers or untrusted-lowers; nvx_scratch_journal, allowed or forbidden; nvx_first_partition_bound, a GPT disk's bound in bytes; and nvx_ram_upper_percent, a RAM upper's cap from 1 to 90 percent of the VM's memory. RootLayout names each as an Option that renders no token when it is None, so that the guest keeps its default. RootLayout::parse reads them strictly: each once, with a value in its decimal or named form, only beside an image, and the journal policy only with a scratch upper, the cap with a RAM upper, and the bound with a GPT image. The shared fixture records the new cases, which nvx-rs and the shell init both replay. guest::root's Upper::Tmpfs takes the cap, mounts the tmpfs with size=N% when it has one, and refuses a cap outside 1 to 90 percent before anything changes, since the kernel reads 0 as no cap. NVX's shell init reads the four tokens with the parser's rules and messages, mounts a capped RAM upper with size=N%, and refuses, before it mounts anything, untrusted lowers and a forbidden journal, which it cannot assemble, as it refuses a GPT image. The sandbox plan names none of the settings, so its command lines stay as they were, and an adapter's plan refuses a setting that its command line names, since it reads the root's tokens back. A clone's activation assembles its tmpfs without a cap, since its record names none, and the runtime adapter's worker hands the cap of its boot tokens to its root. BOOT_CONTRACT_VERSION stays 5, since nothing deployed renders the new tokens. The public API snapshots list the new items. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
The executor-close fixture kills its surviving Windows child, whose stderr still names a file in its temporary directory. Wait for the process object to signal before cleaning up that directory, using the existing bounded Windows wait helper and the fixture's existing cleanup deadline. Keep the workload timeout, the measured close bound, and every existing test assertion unchanged. A child that does not exit now fails explicitly instead of racing the temporary-directory cleanup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Wait for the real fixture to publish its boot readiness and for its descendant to publish its PID before starting the existing half-second workload timeout. Keep that startup wait bounded and clean up the contained processes if readiness fails. Accept the runner's raw TimeoutError and its RuntimeError wrapper only with the expected half-second workload-timeout message. Require a live, positive descendant PID before the workload and retain the check that it has exited after the timeout. Production code, measured deadlines, and skips do not change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Apply the repository formatter's quoting and wrapping to the approved fixture correction. Its parsed Python syntax tree is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Read the root settings before the init refuses GPT images, so an invalid unsigned bound has the same numeric diagnostic in either token order. Keep GPT unsupported and pin the existing image-first overflow fixture's shell diagnostic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Join the unchanged declared input history with the actual current SL2.4a1 source. Both parents have the same input tree; this adds no implementation. The original two owned commits remain outside the input anchor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c390c333-e511-4ec5-a133-8352cb86d485
Compose root settings, a byte-preserving view, the export's mapping count and the unchanged caller command line in one pure cold-boot renderer. Parse the new view tokens strictly without changing legacy consumers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c390c333-e511-4ec5-a133-8352cb86d485
Document the byte-preserving view codec and pure kernel command-line renderer alongside the other module contracts. Clarify that caller text is appended unchanged rather than checked for token collisions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c390c333-e511-4ec5-a133-8352cb86d485
Check caller boot tokens with the existing kernel and init readers while leaving other guest tokens caller-owned. Expose the exact export mapping table and map codec, retaining the protocol-root re-exports, and move the kernel command-line budget into configuration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0aa8163c-8d27-4d54-be61-d9b23c1bc15c
Document the reserved boot-token names, unchanged caller-owned tokens, pure mapping table, public map codec and configuration command-line budget so the crate specification matches the configuration checks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0aa8163c-8d27-4d54-be61-d9b23c1bc15c
Derive whole cold-boot arguments from Runtime and VmConfig, borrowing only the caller-chosen output names. Retain the console device and configured NIC, lower block devices by path, and check the complete Windows command-line limit on every host. Move the portable counting helpers to config while retaining the legacy launch paths and shared argument assembly. Add the reserved configuration-only check and document status-bearing guest power-off behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 76d8c9ca-6594-44b1-bc7b-810b64e7fe51
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 76d8c9ca-6594-44b1-bc7b-810b64e7fe51
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 98b02bc4-2d86-490f-8241-2234ccd39feb
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 98b02bc4-2d86-490f-8241-2234ccd39feb
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 98b02bc4-2d86-490f-8241-2234ccd39feb
Reuse the pure disk-path validator for layered image and scratch arguments before either configuration preflight or whole rendering returns. Move the shared guard into config and retain its sandbox re-export, preserving diagnostics and host module layering. Add a regression for disk delimiters and bracket balance through both entry points, including valid literal paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 76d8c9ca-6594-44b1-bc7b-810b64e7fe51
Create an inspectable cold launch with its owned log, capability, outputs, arguments, and opened resources without starting a process. Refuse restores and missing boot data, preserve existing outputs, remove only newly created output on failure, and check the complete portable command-line limit. Keep the whole-configuration renderer internal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 98b02bc4-2d86-490f-8241-2234ccd39feb
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 98b02bc4-2d86-490f-8241-2234ccd39feb
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 98b02bc4-2d86-490f-8241-2234ccd39feb
Pure local producer foundation: retain the recorded SL2.7/native14dc ancestry and carry only the genuine d1 representability correction after owned8707 readers END. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 98b02bc4-2d86-490f-8241-2234ccd39feb
Carry genuine producer-only d1 input above the recorded pure foundation. Preserve all own cold-create code, API, rollback tests, metadata, and history; current qualification is now required for this effective tuple. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 98b02bc4-2d86-490f-8241-2234ccd39feb
Reject Linux listener-option delimiters and socket names of 108 bytes or more before creating any cold-launch output. Cover both console configurations, Unicode byte boundaries, real valid socket binds, and preservation of caller inputs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 98b02bc4-2d86-490f-8241-2234ccd39feb
Preserve the owned external-command handoff while incorporating the corrected cold-launch renderer and pre-output endpoint checks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4820e3a0-adc3-4382-8ccc-ae8b30d282d0
Contributor
This pull request changes existing testsIt modifies or removes 47 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
Test data
Relocated unchanged: 1; added: 53. Compared the merge base |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The launcher can resolve relative artifact paths against the wrong working directory and report success despite a nonzero VMM exit status.
2 open issues
What changed in this PR
This PR advances #1076 by letting external callers take ownership of a prepared cold VM launch, building on the preparation API from #1342 without adding spawning to host::vm.
Changes:
- Add
Prepared::into_command(), owningVmmCommand, and platform-owned inherited resources. - Add a compiled external-launch example that boots a guest and checks its response and teardown report.
- Document the handoff contract, test ownership preservation, and update both public API snapshots.
| File | Description |
|---|---|
nvx-rs/src/host/vm/tests.rs |
Test resource identity, captured launch inputs, and platform ownership conversions. |
nvx-rs/src/host/vm.rs |
Add owning command handoff and inherited-resource types. |
nvx-rs/README.md |
Document command ownership, launcher obligations, and example usage. |
nvx-rs/public-api/windows.txt |
Record the new Windows public API surface. |
nvx-rs/public-api/linux.txt |
Record the new Linux public API surface. |
nvx-rs/examples/external-launch.rs |
Demonstrate a caller-owned cold launch without a sandbox store. |
nvx-rs/Cargo.toml |
Register the example with the required host feature. |
Review effort: Balanced
Resolve all three artifact paths against the caller before preparing the VM's working directory. Expose the existing status-bearing launch wait to host consumers and require actual VMM success alongside the guest report and teardown. Cover host-only timeout, zero/nonzero status and repeated waits with the existing public handoff fixture. Preserve non-Unix cfg compilation and document the reaped-child lifetime; regenerate both API snapshots. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4820e3a0-adc3-4382-8ccc-ae8b30d282d0
Enrique Saurez (esaurez)
force-pushed
the
esaurez/slim-2.2.1-sl2.8-20261107
branch
2 times, most recently
from
November 8, 2026 20:02
e2d5624 to
49ee94b
Compare
Pedro Henrique Penna (ppenna)
force-pushed
the
esaurez/slim-2.2.1-sl2.8-20261107
branch
from
November 8, 2026 21:25
1017e0d to
0701ea5
Compare
Base automatically changed from
esaurez/slim-2.2.1-sl2.8-20261107
to
feature/nvx-refactor
November 9, 2026 01:22
This was referenced Nov 10, 2026
Enrique Saurez (esaurez)
deleted the
esaurez/slim-2.2.1-sl2.8b-20261107
branch
November 13, 2026 17:55
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 #1076.
Add SL2.8b's
Prepared::into_command()and owninghost::vm::VmmCommandso an external caller can launch the exact command that cold preparation checked. Move the already-opened log, inherited resources, arguments, capability and output names without reopening, cloning handles, rerendering or rediscovering runtime inputs.InheritedHandleowns UnixOwnedFd/ WindowsOwnedHandle, with move-only standard ownership conversions and safe borrowed views.Capture the program, working directory and breakaway policy during preparation. Preserve the parent's creation, rollback, renderer and native preflight behavior. No restore, process registry, Sandbox, producer configuration or native pin changes.
Add a Cargo-compiled
external-launchpublic-API consumer. It resolves all three artifact paths against the caller's directory before preparation, uses the existing low-level launch primitive without a sandbox store, boots Alpine, checks a real shell response, and requires the VMM's actual exit status 0 as well as a successful guest outcome and complete teardown.Review corrections reuse the existing status-bearing
Launched::wait_timeoutas a public host API, with its coupled Unix helpers and Windows imports/private waiter available withoutcli. This minimal API extension was explicitly approved; spawn, allowlisting and process-identity behavior remain unchanged. Regenerate both platform API snapshots.Stacked on #1342 at
7c4156adin preserved native stack 1343, above #1334. The normal input merge retains both original owned commits and current parent corrections. Official whole-layer measurement including these fixes: 268 core lines, 5 counted files, 10 raw paths. The compiled example counts as production; docs/tests/generated snapshots are disclosed separately. The original 160-line estimate is exceeded, but the 320-line stop and 5-counted-file project limit are respected.Tests
Keep four focused ownership tests for moved vectors/resources, the same opened log after path replacement, captured launch inputs and platform-owned conversions. Adapt the original opened-resource test to owning inherited storage without weakening its lifetime assertions.
Add a focused public-API regression in the existing
checked_handoffintegration test. Its real fixture checks zero-timeout while the VMM is running, actual statuses 0 and 17, and repeated retained status reads on Linux and Windows; reuse the existing fixture cleanup guard. No existing assertions or deadlines are removed or weakened.Validation
ae25aa40: CLEAN. Its coupled Unix/non-Unix cfg and reaped-child lifetime documentation follow-up was also independently reviewed CLEAN and passed focused Windows/Linux host Clippy/status tests and Windows host rustdoc. The prior clean5c89457ereview is separate evidence, not approval of the correction.5c89457ehad fresh 17 Windows / 18 Linux host checks, current source-f4b2cceeCLIs/examples, CLI units (3), whole adversarial tests (84), host tests (4) and help checks (8) per platform. Those release-CLI results are explicitly prior-head evidence, not rebuilt correction-head CLI results. Its Linux copied-executableETXTBSYrecovery and other initial failures remain recorded.2a4a7379, including the full 1,818-unit corpus and guest input4050fa15. Current review-fix host/API/example tests and actual relative-path cold runs above are fresh.template/clone/launch.rs:366use ofInherited::new, which exists only on Unix/Windows. After restoring the missing pinned WASI standard target, this precise pre-existing failure was preserved; no unrelated template/allowlist fix or WASI pass is claimed.Native
14dc4116, kernel and Alpine payload identities are unchanged and hash/provenance-validated. Native reuse is bounded artifact-level source/origin/ABI/hash/sidecar evidence; its historical compiler/full recipe is unavailable and not reconstructed. KVM and unchanged full producer distro/performance matrices were not replayed. This remains a Draft; no CI result is claimed or manually queried/triggered.