Skip to content

nvx-rs slimming 4.2.1: the code that stays stops using what leaves #1120

Closed

Description

Parent: #1087, in #1076. Needs: #1091, #1102, #1105, #1119. Unblocks: #1121, #1123, #1130, #1131, #1132. No PR of this group merges before the API-ready commit.

Goal. Before the deletions, retained code stops using what leaves: strict snapshot reads use host::sys; the artifact modules and snapshot key import host::runtime; and PreparedLaunch takes Inherited from its low-level owner. Retained tests stop using old launch helpers. The old capture steps go; guest::snapshot's repair and the time ABI's capture and restore surface become private. nvx-ingress start spawns its announcer with std::process::Command and the private OS helpers, so Announcer, PROGRAM and StartError go before the process modules.

PRs, in order. Each PR has one goal, leaves dev green, and names in its description each old test of its area as kept, rewritten or retired, with each change that check_test_changes.py reports. Its tests follow the repository's test layout: a module's unit tests, and the code that only they use, sit in its tests.rs or tests/ and move with it. The estimates count core lines (code, not comments, docs or tests) and counted files; each PR records its measured values. Each PR does what its Scope says and no more: a case, input or layout that its Scope doesn't name, and whatever "Not in this PR" lists, is out of scope. Address every review comment within the scope, and ask before acting on one that asks for more. A PR whose measured core lines pass the stop that its Estimate names (twice the estimate, at most 600) stops and asks before continuing.

  1. SL6.0a: Strict snapshot reads survive the stores' removal.

    • Objective: Strict snapshot manifest reads currently depend on file helpers and refusal classification owned by the store that later PRs delete. They move to the host's platform layer, preserving the size bound and regular-file checks, and retained readers use their new owner.
    • Contents: open_regular and read_at_most, with their regular-file check, leave host::store::regular for the host's platform layer (host::platform), which stays and already holds the host's file helpers; the store keeps aliases, and SnapshotManifest::read uses the new module. It needs no seal or planner.
    • Scope:
      • Move open_regular, read_at_most, is_refused, the private refusal type and their regular-file support from nvx-rs/src/host/store/regular.rs to host::platform.
      • Keep open_regular_in in the store, but have it build link and non-regular-file refusals with the moved support.
      • Keep temporary aliases in host::store::regular for store and clone callers until nvx-rs slimming 4.2.2: the old clone path, template store and sandbox store are gone #1121 deletes them.
      • Change SnapshotManifest::read in nvx-rs/src/host/template/manifest.rs to use host::platform directly.
      • Preserve the current maximum-length error and the refusal of directories and other non-regular files.
    • Not in this PR:
      • Sealing files, planning paths or adding identity checks: the manifest reader needs none of them.
      • Moving open_regular_in, identity_of, was_removed or unrelated store helpers: no retained caller listed for this PR needs them.
      • Changing the manifest format, length limit or error semantics.
      • Reworking store callers to use the new owner: the aliases keep them working until their deletion.
    • Approach: Move the producers, their private refusal type, classification and regular-file support together into host::platform, without introducing a file abstraction or planner. Let open_regular_in call the moved support, retain aliases at the old path for temporary callers, and update only the retained manifest reader directly.
    • Done when:
      • SnapshotManifest::read reads through host::platform and still accepts a bounded regular manifest.
      • Manifest tests still reject a non-regular path and a manifest over MAX_MANIFEST_LEN.
      • The old clone path still recognizes errors from the moved helpers as refusals.
      • The remaining store callers compile through the temporary aliases.
    • Needs: SL2.12b, in nvx-rs slimming 2.3.2: snapshots have one strict format, and a capture finishes into it #1102.
    • Estimate: 150 core lines, 4 counted files. Past 300 core lines, stop and ask before continuing.
  2. SL6.0b: Retained modules no longer import legacy identity or handle paths.

    • Objective: Retained artifact, snapshot-key and prepared-launch code imports identities and inherited handles through modules that later PRs delete. Each import switches to the low-level module that owns the same type, with no behavior change.
    • Contents: The artifact modules (artifacts/{mod, dir, identity}.rs) and the snapshot key import host::runtime, and PreparedLaunch takes Inherited from its low-level owner, not the supervisor.
    • Scope:
      • Change nvx-rs/src/host/artifacts/{mod,dir,identity}.rs to import ArtifactIdentity and RuntimeIdentity from host::runtime.
      • Change nvx-rs/src/host/template/key.rs to import its retained identity types from host::runtime.
      • Change nvx-rs/src/host/sandbox/prepared.rs so PreparedLaunch takes Inherited from its low-level owner instead of host::supervisor.
      • Keep artifact discovery, identity comparison, snapshot-key construction and prepared-launch behavior unchanged.
    • Not in this PR:
    • Approach: Replace the five imports with the types' retained owners and make only compiler-forced path edits. Do not add a compatibility module, conversion layer or new trait; these are the same types at their established low-level location.
    • Done when:
      • The artifact module tests pass without importing identities from host::store.
      • Snapshot-key tests pass without a store identity import.
      • Prepared-launch tests pass without importing Inherited from host::supervisor.
    • Needs: SL2.12a, in nvx-rs slimming 2.3.2: snapshots have one strict format, and a capture finishes into it #1102.
    • Estimate: 30 core lines, 5 counted files. Past 60 core lines, stop and ask before continuing.
  3. SL6.0c: The tests that stay no longer use the old launch helpers.

    • Objective: Tests that remain after the old store and launch paths disappear still use their helpers. The retained fake VMM, process, Windows junction and spawn-lock cases become independent of those helpers while preserving the behaviors they test.
    • Contents: The fake VMM's own integration cases, runs_boot_or_restore_a_microvm_on_the_terminal and runs_find_absolute_policy_paths_in_relative_share_roots, leave tests/fake_openvmm.rs, with their helpers, for tests/fake_openvmm_behavior.rs: they spawn the fake with std::process::Command and name aggregate children by literal, so they use no supervisor, store, instance or old aggregate renderer. The instance's cases stay for SL6.1 to retire. The retained process, Windows junction and spawn-lock tests replace spawn_command with a test-only spawn that keeps the OS spawn lock's synchronization. They keep their assertions of --restore-network-policy; the Windows spawn-lock tests stay in the Windows platform's tests.rs.
    • Scope:
      • Move the fake VMM's runs_boot_or_restore_a_microvm_on_the_terminal and runs_find_absolute_policy_paths_in_relative_share_roots cases and their helpers from nvx-rs/tests/fake_openvmm.rs to nvx-rs/tests/fake_openvmm_behavior.rs.
      • Have those two cases use std::process::Command and literal aggregate-child names, with no store, supervisor, instance or aggregate-renderer dependency.
      • Replace spawn_command only in retained process, Windows junction and spawn-lock tests with a test-only spawn that holds the OS spawn lock over process creation.
      • Keep the retained assertions for --restore-network-policy, and keep the Windows spawn-lock cases in the Windows platform's tests.rs.
    • Not in this PR:
      • Moving or rewriting the instance's integration cases: SL6.1 retires them with host::instance.
      • Changing production spawning, locking or aggregate naming; this PR changes tests only.
      • Building a reusable test launcher or aggregate-name abstraction: the retained cases need one direct spawn and literal names.
      • Adding new fake VMM behaviors or launch scenarios beyond the two named cases.
    • Approach: Split the two retained fake VMM behaviors from the instance suite, then use direct standard-library spawning. Put the narrow test-only spawn around the existing OS lock beside its callers, in host::tests and the Windows platform's tests module; do not keep it in a production file or preserve spawn_command through another abstraction.
    • Done when:
      • The two named fake VMM cases run from tests/fake_openvmm_behavior.rs and use none of the old launch helpers.
      • Retained process and Windows junction tests call no spawn_command.
      • Windows spawn-lock tests still exercise synchronization and run from the Windows platform's tests.rs.
      • The retained cases still assert --restore-network-policy.
    • Needs: nothing.
    • Estimate: 0 core lines (tests), no counted files.
  4. SL6.11c: The old capture steps are gone.

    • Objective: The one-call guest snapshot path has replaced the old capture-step API, while repair and restore-only time details remain unnecessarily public. The obsolete steps disappear and the remaining implementation details become private to guest::snapshot.
    • Contents: Delete the activation's old capture steps: AdmissionFence, CapturePreparation (prepare), CaptureBarrier (cancel), Restored (into_parts) and CaptureError (into_cause); guest::snapshot::{repair, Repair} become private. TimeAbi::{capture, restore_finish} and the surface that only they use become private to guest::snapshot: CaptureRequest, CaptureOutcome, Restore (with network), RestoreNetwork, RestoreFinish, MemoryRange, CpuSet and ScratchPolicy, with all their methods, such as CaptureRequest::finish, Restore::take_seed and RestoreFinish::new_cpus.
    • Scope:
      • Delete AdmissionFence, CapturePreparation::prepare, CaptureBarrier::cancel, Restored::into_parts and CaptureError::into_cause from the activation code.
      • Make guest::snapshot::{repair, Repair} private while keeping the repair implementation and its internal tests.
      • Make TimeAbi::{capture, restore_finish} private to guest::snapshot.
      • Privatize CaptureRequest, CaptureOutcome, Restore with network, RestoreNetwork, RestoreFinish, MemoryRange, CpuSet and ScratchPolicy, including their methods.
    • Not in this PR:
      • Changing guest::snapshot::capture, its failure disposition or its one-call capture and repair behavior.
      • Redesigning repair or time synchronization after making their implementation surface private.
      • Deleting TimeAbi methods used outside snapshot capture and restore.
      • Adding replacement public capture-step types or compatibility aliases; no retained caller needs them.
    • Approach: Delete the named obsolete items, narrow visibility on the exact repair and time surface, and update only internal paths forced by privacy. Keep the existing capture and repair implementation in place; do not introduce a facade, replacement state machine or generalized private API.
    • Done when:
      • Tests whose sole subject is a deleted capture step or public repair are retired.
      • guest::snapshot::capture tests and repair's internal tests pass.
      • Capture and restore smoke tests pass through the one-call path.
      • The named repair and time items are no longer public outside guest::snapshot.
    • Needs: SL5.2, in nvx-rs slimming 4.1.2: the test adapter is gone #1119; SL7.4b, in nvx-rs slimming 2.4.2: activation holds no fence, and a capture is one guest call #1105.
    • Estimate: 30 core lines, 4 counted files. Past 60 core lines, stop and ask before continuing.
  5. SL6.13c: nvx-ingress start spawns its announcer itself.

    • Objective: nvx-ingress start is the last user of the announcer spawner, which depends on process modules scheduled for deletion. The program performs the same shared port search and detached launch itself, then the obsolete library spawner is removed and the program-only module is hidden.
    • Contents: nvx-ingress start binds the announcer's privileged source port with the shared bind_privileged search, and spawns nvx-ingress inherit GATEWAY through std::process::Command and the private OS layer's helpers, with the launch that Announcer::start gives it today: the socket as its standard input, a session of its own, and every signal's default disposition, none blocked. It exits once the program runs, and fails when the program does not start, as today. The shared bind_privileged stays for the program; no copy is added. Then Announcer, PROGRAM and StartError go, and guest::ingress holds only the program, so the module becomes hidden; its main was already hidden by SL1.5.
    • Scope:
      • Change nvx-ingress start to bind the highest free privileged UDP source port from 1023 down to 512 through the existing shared bind_privileged search.
      • Spawn nvx-ingress inherit GATEWAY with std::process::Command and the private OS layer's helpers, using the bound socket as standard input.
      • Give the child a session of its own, reset every signal to its default disposition and leave none blocked, matching today's launch.
      • Return success once the program runs and failure when it does not start; delete Announcer, PROGRAM, StartError and their tests.
      • Keep the shared privileged-port search for the program, add no copy, and hide guest::ingress, whose only public item is its already-hidden main.
    • Not in this PR:
      • Waiting for the announcer's first scan or announcement: today's start returns once the program runs.
      • Adding retries beyond the existing descending search for ports already in use.
      • Changing inherit, announcement contents, gateway parsing or test mode.
      • Creating a general guest process wrapper or a private copy of the privileged-port search.
      • Changing nvx-init's separate announcer ownership.
    • Approach: Inline the existing start sequence in the program's start path: reuse bind_privileged, configure one standard-library command with the private OS helpers, and wait only for exec success. Delete the old spawner and hide the program-only module rather than extracting another wrapper or copying the port search.
    • Done when:
      • nvx-ingress start binds a privileged source port and starts nvx-ingress inherit with that socket as standard input.
      • Tests show the child has its own session, default signal dispositions and no blocked signals.
      • start exits with 0 after a successful launch and with 1 when the program cannot start.
      • Announcer, PROGRAM and StartError are absent, and guest::ingress is hidden with only its hidden main public.
    • Needs: SL7.3, in nvx-rs slimming 2.4.2: activation holds no fence, and a capture is one guest call #1105; SL1.5, in nvx-rs slimming 1.1.1: the public API is checked, and NVX's programs are hidden from it #1091.
    • Estimate: 80 core lines, 3 counted files. Past 160 core lines, stop and ask before continuing.

Tests. The strict reader, artifact, snapshot-key and prepared-launch tests pass. Tests whose only subject is a deleted capture step retire; capture, repair and smoke tests pass. The fake OpenVMM's cases and retained process, Windows junction and spawn-lock tests call no helper that later groups remove. nvx-ingress start binds a privileged source port, starts nvx-ingress inherit in its own session with the socket as standard input, and keeps its exit behavior; the tests of Announcer retire.

Done when: every PR above has merged, with dev green; retained code reaches no store or supervisor path; tests that stay call no old launch helper; the old capture surface is not public; and guest::ingress is gone.

Activity

  1. esaurez commented on Nov 10, 2026

    @esaurez
    ContributorAuthor

    Workstream source/progress checkpoint - 2026-11-10 UTC

    Checked boxes below mean merged scope, not merely an implemented first iteration. Implementation branches are source checkpoints, not automatic qualification, Ready or merge approval. Some starting inputs are frozen/anonymous commits rather than standalone branches; their exact SHAs and known merged producer refs are recorded explicitly.

    • SL6.0a - First implementation committed; not merged.

      • Published branch: esaurez/slim-4.2.1-sl6.0a-20261108; exact head 6231b87645443fc514f4dfe9ea7b378928cbb474.
      • Recorded starting/base ref: esaurez/slim-2.3.2-sl2.12b-20261107; exact commit 1a6f6ffef2839359307f23fdf015b4b98b9b26ff.
      • Logical prerequisites from the task contract: SL2.12b; use the pinned actual inputs above, not moving branch tips.
      • Implementation completion is separate from qualification and merge. Retain source/input-bound evidence; skips, unavailable checks and prior-head results are not new passes.
      • Previously saved implementation materialized as this commit with its exact source tree unchanged; no new runtime/CI pass is implied.
    • SL6.0b - First implementation committed; not merged.

      • Published branch: esaurez/slim-4.2.1-sl6.0b-20261108; exact head b9d3c3ae0512ad97d67b2328d2ff50646dc2b9dd.
      • Recorded starting/base ref: no standalone starting branch recorded (the pinned commit is reachable through this published task branch); exact commit 489ce50efc93fe253d145557482c6c93e384391b.
      • Logical prerequisites from the task contract: SL2.12a; use the pinned actual inputs above, not moving branch tips.
      • Local qualification is recorded for this implementation's original source/runtime input tuple. Integration and current repository feedback remain separate; this is not a Ready/merge claim.
    • SL6.0c - merged via pull request 1294.

    • SL6.13c - First implementation committed; not merged.

      • Published branch: esaurez/slim-4.2.1-sl6.13c-20261108; exact head fb5b5693b4d57055c1997afb1b24e2343f7e83ba.
      • Recorded starting/base ref: esaurez/slim-2.4.2-sl7.3-20261107; exact commit d4bcd5d5fb64bcc1639d95a715a246aa81293641.
      • Logical prerequisites from the task contract: SL7.3, SL1.5; use the pinned actual inputs above, not moving branch tips.
      • Implementation completion is separate from qualification and merge. Retain source/input-bound evidence; skips, unavailable checks and prior-head results are not new passes.
      • Previously saved implementation materialized as this commit with its exact source tree unchanged; no new runtime/CI pass is implied.
    • SL6.11c - no completed first-iteration handoff recorded. Inspect any preserved partial/prior work before implementing the issue's scope.

    The original technical scope remains authoritative, supplemented only by any portable technical clarification in this issue. No machine-specific execution policy is part of this checkpoint.

  2. ppenna commented on Nov 12, 2026

    @ppenna
    Contributor

    Added slices: SL6.0d and SL6.0e. While preparing SL6.1 (#1121), we found code that stays and still uses what SL6.1 deletes. Each moves that code off host::store, host::instance or host::supervisor first. They are kept-code pull requests under the size rules, with no behavior change.

    • SL6.0d: the snapshot and CLI code stop using host::store's file helpers.
      • write_private, sync_dir and create_new move to host::platform.
      • finish_capture (host/snapshot/capture.rs), host/cli/store/files.rs, host/cli/snapshots.rs and host/cli/snapshots/capture.rs use them there, along with the regular aliases' replacements.
      • Estimate: about 75 core lines in 6 counted files. The sixth file is store/files.rs, which re-exports the moved helpers until SL6.1 deletes the store, so a split would be harder to follow.
    • SL6.0e: code that stays stops using the instance's and the supervisor's identities.
      • generate_capability becomes crate-private in host::vm; integration tests draw their own random capability.
      • The CLI gets its own StopOutcome, and creates its state directory through platform::create_store_dir, with the same messages.
      • Estimate: about 39 core lines in 3 counted files.

    This group closes once SL6.0d, SL6.0e and SL6.11c merge.

  3. ppenna commented on Nov 12, 2026

    @ppenna
    Contributor

    Added slice: SL6.0f, "the CLI checks the command line it launches". This is a preparatory kept-code PR, found while preparing SL6.3 (#1122).

    sandbox start and sandbox run still check the user's command line through SandboxLaunch and SandboxPlan (check_command_line in host/cli/sandbox/lifecycle/start.rs). run still uses host::sandbox's export_command_line_fragment, and SANDBOX_COMMAND_LINE_MAX_SIZE, which is built from host::sandbox::OPENVMM_COMMAND_LINE_RESERVE. SL6.3 deletes all three.

    • Scope: the CLI checks the command line it actually launches. run's export fragment and size limit move off host::sandbox. Behavior and messages are unchanged, with tests.
    • Estimate: 50–80 core lines in 2–3 counted files. Stop and ask past 160 lines or 5 files.

    This group now closes once SL6.0d (#1518), SL6.0e (#1519), SL6.0f and SL6.11c (#1504) merge.

  4. 8 remaining items

  5. ppenna commented on Nov 12, 2026

    @ppenna
    Contributor

    Completed. Every slice of this group has merged into feature/nvx-refactor:

    Passing CI that contains all of them: the push run on feature/nvx-refactor at 230a1b0877d, https://github.com/microsoft/nvx/actions/runs/43201313792, including the microVM jobs on KVM, MSHV and WHP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions