Repository navigation
Description
Parent: #1076. Phase: 2, the new interface, beside the old one. Needs: #1077; inside phase 2, groups of #1079 (see the table below). Unblocks: inside phase 2, groups of #1081; #1083, #1086.
Goal. A caller creates a VM in a private directory with fixed, documented names; inspects it, or hands it to its own launcher; spawns it with no child left behind on failure; stops it only with a confirmed exit; proves an exit after its own restart; and verifies and reattaches a running VM from values that it saved.
Today (dev e42b1795) and after:
| Area | Today | After |
|---|---|---|
| VM directory | Store-owned layouts: SandboxStore uses fixed internal names, but callers get no public VM directory or outputs. |
VmDir, created private (0700, or an owner-only protected DACL) by host::vm::create_private_dir, with fixed, documented names. VmOutputs names the log, the outcome report, the control endpoint and the console. nvx generates the Capability and returns it, and never writes it; its Debug is redacted, and as_bytes gives its bytes. |
| Launch | A store-coupled supervisor. | host::vm::create returns Prepared (the capability, the outputs, the handles to inherit, the opened log and the arguments), then host::vm::spawn returns Launched. A cold create(from: None) requires VmConfig::boot. InheritedHandle owns each handle added through Prepared::inherit, with conversions from the platform's owned descriptor or handle. A caller can instead turn Prepared into a VmmCommand with into_command(), for its own launcher. |
| Restore-ready event | Inside the clone path. | protocol::restore_ready::parse_ready_event parses OpenVMM's exact restore-ready event. restore::ReadyListener, with into_std, is a socket in the VM directory on Linux or a pipe on Windows, and accepts only the VM's process. #1081 binds it before a restore's spawn. |
| Stop | Through the store. | host::vm::stop returns Stopped { self_exited, forced, report } only after a confirmed exit; otherwise it fails with the error kind StillRunning. |
| Exits | Process::wait returns a bool. |
Process::wait(deadline) returns Option<Exited>; Process::open returns Opened::{Running, Exited}; Process::exit_handle gives async callers a duplicated pidfd or process handle, or none when the host only polls; and report::read returns Result<Option<Report>>. |
| Reattach | Through the sandbox store. | host::vm::{SavedVm, verify, reattach, Reattached}, with the runtime identity check: verify checks the identity and the process without connecting, and reattach then connects and attaches; another runtime is an error of the new kind RuntimeMismatch. host::transport::{Transport, connect_verified, HostConnection} checks who serves the endpoint before it attaches. |
Groups, in order. Each group issue lists its PRs, with their goals, contents, Needs and estimates.
| Group issue | Delivers | PRs | Needs |
|---|---|---|---|
| #1099 | A VM is created in a private directory, with its restore-ready listener, then spawned | SL2.7, SL2.8, SL2.8b, SL2.17, SL2.9 | #1091, #1098 |
| #1100 | A VM stops only with a proved exit, and can be reattached | SL2.9a, SL2.10, SL2.10b, SL2.11a, SL2.11b | #1099 |
Notes.
- The Windows spawn lock is private to
host::vm::spawn. A Windows caller must not start other processes while a VM starts, for example by holding its own lock aroundhost::vm::spawnand its other spawns. - No half-made VMs. A failed
createremoves what it made; ifspawnfails, no child is left running; if it succeeds, the caller owns a VM that outlives it. - Persist what recovery needs before the spawn.
nvxnever writes theCapability. A caller that recovers or reattaches journalsPrepared::capability()andPrepared::outputs()before the spawn, then records the process reference once the spawn succeeds. - In-place changes update their callers.
Process::waitandreport::readchange their signatures in place; a crate-private wait keeps the old wrappers stable until their callers go in phase 4. - Restores come later.
createrefusesfrom: Someuntil nvx-rs slimming, phase 2.3: one snapshot type, activation record v2, and restores that the caller drives #1081.
Tests. Unit tests of the directory's checks, reserved names and private creation, on both platforms; Capability::as_bytes, its redacted Debug, and no capability file written. A failed create removes everything that it made, and a failed spawn leaves no child. A cold create(from: None) refuses a missing Boot; Prepared::into_command hands over the opened log and the inherited handles without reopening them. stop returns Stopped only after a confirmed exit, and fails with StillRunning when it can't confirm one; an unreadable or malformed outcome report is an Io error in Stopped::report, and a missing one is Ok(None). Process::wait returns None at its deadline, and an Exited proof after the exit; Process::open returns Exited when the recorded process is gone, its ID now belongs to a process with another start time, or the boot has changed; exit_handle gives a duplicated handle, or none on a host that only polls. connect_verified refuses an endpoint that another process serves, and verify and reattach refuse another runtime with RuntimeMismatch, and verify connects to nothing. protocol::restore_ready accepts the exact event and refuses malformed input; ReadyListener::into_std hands the listener over; and, on Linux and Windows, the listener refuses a client that isn't the VM's process. A compiled example launches a cold VM through an external launcher.
Done when: every group issue is closed.
Activity
nvxa library of mechanisms and value types #1076ppenna commentedon Nov 11, 2026
Both groups of this subphase are complete and closed: #1099 (SL2.7, SL2.8, SL2.8b, SL2.17, SL2.9) and #1100 (SL2.9a, SL2.10, SL2.10b, SL2.11a, SL2.11b). Every PR has merged into
feature/nvx-refactor, whose post-merge CI run at394c89025epassed: https://github.com/microsoft/nvx/actions/runs/42952780739. Closing the subphase as complete.