Repository navigation
nvx-rs: build a clone's activation record and launch its restore - #949
Open
Enrique Saurez (esaurez) wants to merge 4 commits into
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Cleanup can delete another running sandbox’s clone files, and clone requests can silently restore the wrong network address.
3 open issues
What changed in this PR
This PR extends NVX’s template-clone planning with activation records and supervised OpenVMM restore launches, implementing part 2 of #844. The Activate, Ready, and Start sequence remains for later PRs.
Changes:
- Builds activation records from clone settings, binding metadata, and template resources.
- Launches restores from leased snapshots with platform-specific readiness endpoints and scratch-claim inheritance.
- Adds clone-file and socket cleanup, with tests for records, durability, and lifecycle behavior.
| File | Description |
|---|---|
nvx-rs/src/host/template/store/leases/tests.rs |
Tests durable cleanup during lease release. |
nvx-rs/src/host/template/store/leases.rs |
Reserves clone sockets and removes launch artifacts on release. |
nvx-rs/src/host/template/store/capture.rs |
Shares snapshot and socket-limit constants. |
nvx-rs/src/host/template/store.rs |
Documents clone sockets within generation payloads. |
nvx-rs/src/host/template/mod.rs |
Exports and documents Restoring. |
nvx-rs/src/host/template/key/tests.rs |
Tests DNS extraction and malformed-token refusals. |
nvx-rs/src/host/template/key.rs |
Exposes template DNS and network-policy metadata internally. |
nvx-rs/src/host/template/clone/tests.rs |
Tests activation records and unsupported settings. |
nvx-rs/src/host/template/clone/record.rs |
Builds activation records and binding identities. |
nvx-rs/src/host/template/clone/launch.rs |
Implements restore launches and the Restoring handle. |
nvx-rs/src/host/template/clone.rs |
Adds activation settings and launch-device planning. |
nvx-rs/src/host/supervisor/mod.rs |
Passes runtime records into clone-socket cleanup. |
nvx-rs/src/host/store/tests.rs |
Tests selective cleanup of clone launch files. |
nvx-rs/src/host/store/mod.rs |
Adds owned clone files, endpoint reservations, and cleanup. |
nvx-rs/src/host/store/lifecycle/tests.rs |
Extends removal tests for clone files and redirected paths. |
nvx-rs/src/host/store/lifecycle.rs |
Removes clone files through the verified directory handle. |
nvx-rs/src/host/store/lease.rs |
Centralizes generation-directory and clone-socket naming. |
nvx-rs/src/host/platform/windows.rs |
Adds private named-pipe readiness listeners. |
nvx-rs/src/host/platform/other.rs |
Adds unsupported-platform readiness stubs. |
nvx-rs/src/host/platform/linux.rs |
Adds Unix-socket readiness listeners. |
nvx-rs/README.md |
Documents activation records, restore launches, and cleanup. |
Review effort: Balanced
A clone's plan builds the activation record that the clone's guest applies in place of the configuration that its template captured, from the request's workload view, overlay mode and journal policy, and from the inputs that the plan checks: a restore section with a fresh transaction ID of 16 random bytes and the lease's template reference; the EROFS image with its binding's identity and the UUID that the adapter's plan expects; a tmpfs, or the scratch image's binding identity with the journal policy; no export; the network of the template's NIC, with the policy identity that its key records, the DNS server that the template's guest configured its resolver with, unpublished listeners and the guest's proxy URL; and the template's processors and the memory target. A binding's identity is the SHA-256 digest of a domain and of the binding's serde form, which reads no file content. TemplateKey::dns_server reads the DNS server from the virtnet_dns= token of the key's effective command line, which OpenVMM adds when the NIC's policy lets the guest reach the gateway's DNS server: once at most, for a template with a NIC, naming one IP address in its canonical spelling. Any other form is a malformed key, which planning a clone refuses as an I/O error. The plan refuses a GPT image, which a clone's guest does not assemble yet, and settings that no record holds, before it opens or creates anything. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a424884c-018a-45f5-a7fe-c57548a873f0
Enrique Saurez (esaurez)
force-pushed
the
esaurez/844-pr2-activation-restore-launch-20261024
branch
from
October 28, 2026 04:38
aa44693 to
49adcfe
Compare
The sandbox store owns two more names for a clone's launch: its activation record, clone-activation.bin, and the socket at which the launch awaits the VMM's restore-ready event on Linux, restore.sock, as long a name as control.sock. A release removes them with the scratch device's file of a clone in RAM, before the clone binding, whose durable removal makes theirs durable too. A removal of the sandbox now does the same: it removes the clone's files with the binding, before the sync that makes the binding's absence durable, rather than with the store's other files after it. Both take a clone's file whose deletion is pending, as a Windows file system without POSIX deletion leaves one that a reader holds, for removed, so a release that failed part of the way can be retried. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a424884c-018a-45f5-a7fe-c57548a873f0
Enrique Saurez (esaurez)
force-pushed
the
esaurez/844-pr2-activation-restore-launch-20261024
branch
from
October 28, 2026 05:34
49adcfe to
42cf6b4
Compare
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Scratch validation can expose shared snapshot artifacts as writable devices, while failed replanning can leave older plans pointing to a missing scratch device.
2 open issues
3 resolved since last review
Review effort: Balanced
ClonePlan::launch runs the launch that a clone's lease reserves, under the lock that acquired the lease, once the sandbox's clone binding records the lease. The VMM restores the generation's snapshot in place, which nothing copies, links, or changes, so clones of one generation restore it at once. OpenVMM requires a restore's control socket in the snapshot's parent on Linux, so the lease reserves the clone's control endpoint there: <launch>.sock in the generation's payload, named after the launch, which the journal and the runtime record name as any launch's endpoint. On Windows the VMM listens at a fresh named pipe, as every launch does. The launch refuses, before it writes anything, a socket that is not beside the plan's snapshot, a payload whose mode is not exactly 0700, a socket path that, with or without the payload's links resolved, would not fit a Unix socket's address, and a control socket path that holds ',' or '=', which end the path in OpenVMM's option listen=<path>. The VMM restores the snapshot in place while the clone's guest writes its scratch image, so a plan refuses a scratch image that is a file of the generation's snapshot, by any path, through a hard link say. The launch also refuses, before it writes anything, a plan in RAM whose scratch device's file is gone, as when a later plan of the clone replaced it and failed. It writes the clone's activation record to clone-activation.bin, listens for the VMM's restore-ready event, and runs supervisor::launch with OpenVMM's restore of the clone's devices: --restore-snapshot with the generation's snapshot, --restore-memory with the memory target, --restore-ready-path, the template's backend, CPU profile, and processors, a boot console that discards its output, as cold boots have, and the control console, without a kernel, command line, or guest address. The VMM inherits a scratch image's claim: its descriptor on Linux, and on Windows the image's guard, once the launch has closed the claim's handle, which OpenVMM's open of the image, sharing no writing, would refuse. The VMM removes its socket as it exits. The end of a launch and the clearing of a runtime record remove one that an abrupt exit leaves, and a release removes it and syncs the payload before it ends the lease, so a payload holds clone sockets only while its generation has leases; a retirement removes the payload with whatever a crash left in it. A release ends the lock's reservation of the launch before it removes anything, so one that fails part of the way leaves no launch of a plan whose files it removed. Tests run the launch with a fake VMM in a process of its own, which listens beside the snapshot on Linux and holds the snapshot open sharing only reads on Windows, as OpenVMM does: the rendered arguments, the record, the claim, two clones of one generation launched at once, the socket's removal at teardown, at release, and after a crash, the refusals before any spawn, and crashes of the launch's caller before it begins, once it began, and once its VMM started. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a424884c-018a-45f5-a7fe-c57548a873f0
…ate's OpenVMM restores a clone's NIC, with the guest's address, from the template's snapshot, and a clone's launch drops the --net of the adapter's plan, so a plan that gave the guest another address or prefix than the template's NIC planned a clone that silently ran on the template's. ClonePlan::new now refuses such a plan with ErrorKind::Unsupported: it compares the plan's network with the guest network of the template key's NIC, TemplateKey::guest_network, in the canonical form that both write. Which policy a template serves stays the network templates' concern, one template per policy, whose key holds its identity. Part of #844. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a424884c-018a-45f5-a7fe-c57548a873f0
Enrique Saurez (esaurez)
force-pushed
the
esaurez/844-pr2-activation-restore-launch-20261024
branch
from
October 28, 2026 06:19
42cf6b4 to
caa6a0b
Compare
| path, binding, &key, &owned, deadline, access, | ||
| )?), | ||
| (Some(path), Some(binding)) => { | ||
| let shared = SnapshotFiles::of(&snapshot)?; |
This branch has not been deployed
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.



Stacked on #888; this PR retargets when its parent merges.
Chain: #936 → #937 → #939 → #940 → #941 → #888 → this PR → #950 → #951, on
dev; #879, #881, #885, #886, #877, #932, #884, #887, #933, and #880 have merged.Part of #844 (PR 2 of 5: the activation record and the restore launch).
What and why
#888's
ClonePlanchecks a clone's inputs against the generation of the template that it leased. This PR builds the clone's activation record from that plan, keeps it in the clone's sandbox, and launches the clone as OpenVMM's restore of the generation's snapshot, as the launch that the clone's lease reserves. The restore sequence, which reads the VMM's restore-ready event, sends the record in the adapter'sActivate, starts the sandbox, and commits the launch, comes in PR 3.The activation record
ClonePlan::activation()is the record that the clone's guest applies in place of the configuration that its template captured. The plan builds it from the request's workload view, overlay mode, and journal policy, and from the inputs that it checks:TemplateReference;A binding's identity is the SHA-256 digest of a domain and of the binding's serde form, never of a file's content.
TemplateKey::dns_serverreads the DNS server from thevirtnet_dns=token of the key's effective command line, which OpenVMM adds when the NIC's policy lets the guest reach the gateway's DNS server: once at most, for a template with a NIC, naming one IP address in its canonical spelling. Any other form is a malformed key, which planning a clone refuses. The plan also refuses a GPT image, which a clone's guest does not assemble yet, a network on another guest network than the template's NIC, whose address OpenVMM restores from the snapshot while the launch passes none, and settings that no record holds, before it opens or creates anything. Nor may a scratch image be a file of the generation's snapshot, by any path, hard links included: every clone of the generation restores the snapshot in place, while its guest writes its scratch image.The record in the sandbox
The sandbox's store owns two more names:
clone-activation.bin, the record, andrestore.sock, the socket at which the launch awaits the VMM's restore-ready event on Linux, as long a name ascontrol.sock. A release first ends the lock's reservation of the clone's launch, so that one that fails part of the way leaves no launch of a plan whose files it removed, then removes them withclone-scratch.raw, before the clone binding, whose durable removal makes theirs durable too. A removal of the sandbox does the same: it removes them through the handle of the sandbox's directory with which it checks the sandbox's lock and removes the binding, so that no change of a link in the path redirects their removal to another sandbox, and before the sync that makes the binding's absence durable. On a Windows file system without POSIX deletion, a file of the clone whose deletion is pending, because a reader holds it, lingers as a pending binding does until the reader closes it, rather than being refused as an entry that is not the store's, and a retried release takes it for removed.The restore launch
ClonePlan::launch(self, &mut Acquired, openvmm, &RuntimeIdentity) -> Restoringruns the launch that the clone's lease reserves, under the lock that acquired the lease, once the sandbox's clone binding records the lease. It:clone-activation.bin, from which a caller resends it unchanged after a crash;restore.sockon Linux, and at a named pipe of a random name on Windows;supervisor::launchwith OpenVMM's restore of the clone's devices:--restore-snapshotwith the generation's snapshot in place, which nothing copies, links, or changes, so that clones of one generation restore it at once;--restore-memorywith the memory target;--restore-ready-path; the template's backend, CPU profile, and processors; a boot console that discards its output, as cold boots have; and the control console, but no kernel, initramfs, command line, or guest address, which the snapshot fixes.OpenVMM requires a restore's control socket in the snapshot's parent on Linux, so the lease reserves the clone's control endpoint there:
<launch>.sockin the generation's payload, named after the launch that the lease reserves, which the journal and the runtime record name as any launch's endpoint. The launch refuses, before it writes anything, a socket that is not beside the plan's snapshot, a payload whose mode is not exactly 0700, a socket path that, with or without the payload's links resolved, would not fit a Unix socket's address, and a control socket path that holds,or=, which end the path in OpenVMM's optionlisten=<path>. On Windows, the VMM listens at a fresh named pipe, as every launch does. For an upper in RAM, the launch also checks, before it writes anything, that the scratch device's file that the plan created is still a regular file of the template's scratch geometry: a later plan under the same acquisition replaces it, and removes it if that plan fails.The VMM inherits a scratch image's claim: its descriptor on Linux, and on Windows the image's guard, once the launch has closed the claim's handle, which OpenVMM's open of the image, sharing no writing, would refuse.
The VMM removes its socket as it exits. The end of a launch and the clearing of a runtime record remove one that an abrupt exit leaves, and a release removes it and syncs the payload before it ends the lease, so a payload holds the sockets of its clones only while its generation has leases; a retirement removes the payload with whatever a crash left in it. The sandbox's store recognizes a clone's socket by its path in the payload of the generation that the binding's lease names,
generations/<generation>/<launch>.sock. Since the sandbox's store builds on nothing of the template module, it makes that check in its ownleasemodule, beside the lease, where the name of the generations' directory,GENERATIONS_NAME, now lives too; the template module re-exports it at its public path, as it re-exports the lease.Not in this PR
Validation
The gates at this head, caa6a0b, are pending until they finish.
Per commit, on Linux x86_64 and on Windows: fmt and Clippy with
-D warningsfor nvx-rs in CI's three configurations (--all-targets --all-features,--lib, and--lib --no-default-features),tests/host_modules.rs, and the tests of the template, store, sandbox, supervisor, and platform modules; on Linux, also the aarch64-apple-darwin check.Tests added, with a fake VMM in a process of its own, which listens beside the snapshot on Linux and holds the snapshot open sharing only reads on Windows, as OpenVMM does: the rendered arguments, the record, the claim, two clones of one generation launched at once, the socket's removal at teardown, at release, and after a crash, the refusals before any spawn, including a control socket path with
,or=, and crashes of the launch's caller before it begins, once it began, and once its VMM started. Also: the records of clones of every upper, network, and workload; the DNS server of a key's command line and its refusals; the end of the lock's reservation before a release that fails part of the way removes anything; the removal of the clone's files at release and at the sandbox's removal, durably and through the sandbox's directory, also when a link in its path names another sandbox meanwhile, and, on Windows, while their deletion is pending, also by a retried release; the refusal of a plan of another guest address or prefix than the template's NIC; the refusal of a scratch image that is a hard link to the snapshot's memory; and the refusal of the launch of a stale plan in RAM whose file a failed plan of the clone removed.No VM: a real restore comes with PR 5's VM tests.