Repository navigation
nvx-rs: lower adapter plans into the plans of template captures - #884
Open
Enrique Saurez (esaurez) wants to merge 16 commits into
Open
Enrique Saurez (esaurez) wants to merge 16 commits into
Enrique Saurez (esaurez) wants to merge 16 commits into
Conversation
A clone that the host restores from a template keeps the template's captured configuration and command line, so nothing tells its guest the image, upper, export, network settings, and workload view of its own launch, and nothing identifies the settings that a guest applied (#838). The host, the guest, and every adapter's framing need one record of them, which they all check by the same rules. nvx::activation defines it without features, beside nvx::protocol. An ActivationRecord holds the version of its schema, the restore section of a clone, which names the restore's transaction ID and the template that it restores, by its store generation and the identity of its key, and the Configuration that the activation applies: the image, with the identity of its binding and the UUID or disk GUID that the guest expects; the upper, a tmpfs or the scratch with its binding's identity and journal policy; the overlay mode; one export, a directory or an aggregate, with its plan's identity and its 1 to 4,096 mappings; the network, with its policy's identity, a DNS address, the listeners' publication, and a proxy endpoint; the workload view, its hostname, machine-ID mode, and optional workload identity; and the CPU count and memory size that the guest must have. The record names objects by 32-byte identities that the host computes, and never holds their content; the other inputs of the guest's root assembly are invariants of the template. ReadyActivation is what Ready reports: the configuration identity, and a clone's transaction ID. The limits are public constants: 1,024 bytes for a record, 4,096 mappings, 64 bytes for a hostname, and 255 for a proxy endpoint. ActivationRecord::new admits a configuration only once Configuration::check finds that it keeps them, pairs an export with its mappings, and holds a nonzero UID and GID, a hostname without a NUL, as the guest's entry requires, a proxy endpoint of visible ASCII without an @, and so without credentials, and nonzero sizes. The canonical encoding and the configuration identity follow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8f7bd07b-b09f-48d5-bc49-ad06f47ef3f9
The kernel reads an ID of u32::MAX as "unchanged", so the guest's entry refuses it, and the managed agent ignores such a workload ID on its command line. Configuration::check refused only 0, so a record could hold a workload identity that no guest applies. It now refuses u32::MAX too, for the UID and the GID alike, and the tests take u32::MAX - 1 as the largest ID that a record holds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8f7bd07b-b09f-48d5-bc49-ad06f47ef3f9
The template store publishes each template under a nonzero 64-bit ID, which it assigns in the order of publication and never reuses, and a clone's record names the template that it restores by that ID and the identity of the template's key (#838, #839). StoreGeneration held 16 opaque bytes, so the store would have had to fit its ID into bytes of another shape, by a convention of its own. StoreGeneration now wraps a NonZeroU64: StoreGeneration::new takes one, StoreGeneration::get returns it, two generations of one store compare in the order of their publication, and a generation displays as its decimal number. The identity of the template's key stays 32 bytes, and the transaction ID 16. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8f7bd07b-b09f-48d5-bc49-ad06f47ef3f9
A template's compatibility key must hold the version of every contract through which its clones are restored. A runtime identity records the boot and control versions, but none of those of OpenVMM's snapshot format, the schema of the saved device state, the time ABI, and the restore packet. Add them to nvx::contract with the values of the pinned OpenVMM, and derive the guest's restore-packet decoder version from its constant. The ignored test of tests/openvmm_vectors.rs, which the NVX CLI jobs run with the submodule checked out, now also checks each version against the constant in OpenVMM's source that it must match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
A template is a capture of a microVM from which independent clones are restored. Which clones it can serve, and on which hosts, depends on small metadata: the runtime that captured it, the contracts through which its clones are restored, and the machine that they restore. Stores, adapters, and restores need one key that holds exactly that metadata, which they compare, and whose canonical bytes and identity never vary between hosts or builds. Add nvx::host::template: - TemplateManifest::read reads the manifest that OpenVMM wrote for a capture, at most 1 MiB of a regular file that is not a link or other reparse point, through a strict reader of the protobuf wire format that refuses unknown, mistyped, repeated, and truncated fields. It refuses a capture that is not a template's: a platform-tier capture for clones, with a read-only distro whose identity is unbound, a fresh scratch, and the dormant filesystem slot. - CaptureClass holds the invariant inputs of a capture, known before it, and TemplateKey::new checks that the capture made the machine that its class requested before it derives the key from metadata alone. It never reads or digests an artifact. - A key and a class each have one canonical, versioned encoding, and their identities are domain-separated SHA-256 digests of it. TemplateKey's from_bytes refuses any other encoding. Besides the versions of the snapshot contracts in nvx::contract, a key holds the version of the activation contract, nvx::activation::VERSION, and a key's identity converts without loss to and from the nvx::activation::Identity by which a clone's activation record names its template. The fixtures are manifests that OpenVMM's own writer encoded, so the tests check the reader against OpenVMM's encoding and not only against the test encoder. The vectors of tests/vectors/template-key-v1.txt, which an independent encoder wrote, fix version 1 of the encoding. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The scoped, I/O-free lowering and supporting tests have no identified blocking issues.
No open issues 🎉
What changed in this PR
Adds pure adapter-plan lowering for platform template captures in nvx-rs, the first stage of #841’s capture-and-publication work. This PR prepares capture configuration without launching a VM or publishing a template.
Changes:
- Adds
TemplatePlanandTemplateResources, validating placeholder bindings, resources, network policies, and transaction paths. - Renders dormant devices, tenant-free kernel options, and generation-based snapshot arguments.
- Adds lowering and allowlist tests, verifies copied OpenVMM source excerpts, and documents the API.
| File | Description |
|---|---|
nvx-rs/tests/vectors/README.md |
Documents OpenVMM source excerpts and verification. |
nvx-rs/tests/vectors/openvmm/platform_command_line_token_is_invariant.rs |
Copies the platform token allowlist for tests. |
nvx-rs/tests/vectors/openvmm/microvm_control_tty_command_line.rs |
Copies the control TTY token constant. |
nvx-rs/tests/openvmm_vectors.rs |
Checks source excerpts against the submodule. |
nvx-rs/src/host/template/plan/tests.rs |
Tests lowering, argument rendering, allowlisting, and refusals. |
nvx-rs/src/host/template/plan.rs |
Implements capture plans and resource validation. |
nvx-rs/src/host/template/mod.rs |
Declares and re-exports the plan API. |
nvx-rs/src/host/sandbox/mod.rs |
Makes disk-path validation available within the crate. |
nvx-rs/src/host/mod.rs |
Documents template-plan lowering. |
nvx-rs/README.md |
Describes the new API and capture configuration. |
doc/ci.md |
Documents allowlist excerpt checks in CI. |
.github/workflows/ci.yml |
Updates comments describing existing comparison checks. |
Review effort: Balanced
The row of nvx::host::template ended mid-sentence: the conformance vectors of version 1 pin the key's encoding and identity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
Templates need a store of their own, apart from every sandbox's store, whose generations clones lease and restore, at a location that the adapter chooses. Add nvx::host::template::Store: a private directory with an identity of its own, the immutable payload of each generation, its key's canonical encoding beside the snapshot that OpenVMM published, and a versioned index that lists each generation with the identities of its key and its capture class and its own metadata, such as its time contract, which eligibility and restores read without the payload. Creation is crash-safe, makes one store between concurrent creations, and refuses a directory of anything else. Records are JSON in one format, replaced with the sandbox store's atomic writes, and records of another format fail rather than being migrated. The store's directories must be private to the current user, through the sandbox store's checks with a wording of their own on Linux, and links, and on Windows other reparse points, are refused among its directories and files. Four namespaces stay apart, each with a type of its own: the store generation, nvx::activation::StoreGeneration, which the index assigns in the order of publication and never twice, and by which a clone's activation record names its template; the bound-block storage generation, which belongs to the storage owner; the time lineage counter of the time contract; and the VM generation ID, which the store never records. Publication, which captures a template and indexes it, builds on the store, and the manifest reader now shares the store's bounded reads of regular files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
A generation of a template must stay in its store while a clone may still restore it, and no clone may restore one outside the restore window of its capture's time contract, which OpenVMM refuses only once the restore has begun. Add the eligibility of a capture at a host's time, apart from its compatibility: TimeContract::eligibility measures the downtime as OpenVMM's restore does, by the host's monotonic clock within the boot of the capture and by UTC otherwise, and refuses a negative downtime, and one that would advance the clone's VM time by more than 30 days. HostTime::now samples the identities and clocks of the host as OpenVMM does. Add the leases of a store's generations. Store::acquire leases an eligible generation that no tombstone quarantines to a provisioned sandbox that records no VM, launch, or lease, in one transaction under the store's lock and then the sandbox's: it writes the lease's record in the store, and the same record as the clone binding, clone.json, beside the sandbox's launch journal, and returns the sandbox's lock, under which the caller launches the clone. A lease never expires: Store::release ends it once the sandbox records no VM and no launch, and so once the clone's VM has exited, as the sandbox's store verifies through its recorded process. Store::select picks the latest eligible generation of a key. Store::tombstone quarantines a generation, which takes no new lease while its leases keep working, and Store::retire deletes it once its leases have drained: its payload, then its entry in the index, then its tombstone, and it removes the records of leases that no clone binding names any more. Each of these transactions makes its steps durable in order, syncing directories where the platform can, so that no clone binding outlasts, through a crash of the host, the record that keeps its generation in the store, even when the store and the sandbox are on different filesystems. The sandbox store counts the clone binding among its files, so that a removal of a sandbox removes it, and three error codes name the new refusals: template-ineligible, template-tombstoned, and sandbox-leased. The documentation of foreign-files now covers the template store too. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
Address the review of the leases of the template store: - Tombstones are read and checked wherever the store lists them, so that a tombstone of another store or of another format, or a link, fails a listing and a selection, as it fails an acquisition, rather than quarantining a generation. A retirement removes a tombstone only once it has checked it. - A retirement makes the absence of a clone binding durable in the directory that holds the sandbox's entry, besides the sandbox's own directory: a sandbox that was removed and provisioned again at its path is a new directory, whose entries never held the binding. - The first use of a store's directory of leases or of tombstones in a process also syncs the store's parent, so that no binding names a record of a store whose own entry a crash of the host could lose. - On Windows, a record that a transaction in another process removes as this one opens or removes it is gone, rather than an error: a file system without POSIX deletion refuses every open of a removed file while other handles, such as a scanner's, hold it. A retirement that raced releases failed so, rarely. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
Enrique Saurez (esaurez)
force-pushed
the
esaurez/839-pr2-template-store-20261024
branch
from
October 24, 2026 22:20
401015b to
8718d50
Compare
Enrique Saurez (esaurez)
changed the base branch from
esaurez/839-pr2-template-store-20261024
to
esaurez/839-pr3-leases-20261024
October 24, 2026 22:30
Enrique Saurez (esaurez)
force-pushed
the
esaurez/841-pr1-template-lowering-20261024
branch
from
October 24, 2026 22:30
39ac01d to
79d580b
Compare
On Windows, a refused read or removal of a template store's record counted as a removal by another transaction if an open without access rights was refused too. Win32 opens ask for SYNCHRONIZE and FILE_READ_ATTRIBUTES even then, so a file whose access control list grants neither was refused both ways, and taken for a removed one: a retirement skipped the record of a live lease, or the binding that names it, and deleted the generation, and a tombstone that refused access quarantined nothing. platform::is_deleted now opens the file with NtOpenFile, relative to its directory, whose status tells STATUS_DELETE_PENDING from STATUS_ACCESS_DENIED, which Win32 reports alike, and a refused file counts as removed only if its deletion is pending, or it is gone. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
Enrique Saurez (esaurez)
force-pushed
the
esaurez/841-pr1-template-lowering-20261024
branch
from
October 24, 2026 23:04
79d580b to
7f55573
Compare
A lease named its sandbox by the absolute path through which it was leased. That path could run through a link, or, on Unix, through a directory that a `..` component passes through. Once that link or directory went, the path named nothing, though the sandbox and its clone binding remained, so a retirement took the lease for unbound, and could delete its generation while the clone ran. And a link that changed while the sandbox was leased could have redirected the binding to another sandbox. acquire now resolves the links of the sandbox's path before it takes the sandbox's lock, and the lease, the binding, and the lock that it returns all name the sandbox by that path. The tests that build paths through links now move a sandbox's directory and link its old path after the lease. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
A release took the sandbox's lock through any path. If that path ran through a link, and the link changed after the release checked that no VM runs, the release could remove another sandbox's binding, whose lock it does not hold, so a retirement could then delete the generation that the other sandbox's running clone uses. A release now refuses, before it removes anything, a lock that does not name the sandbox by its lease's path. That path has its links resolved, and acquire takes the lock through it too. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
A removal of a sandbox removed its clone binding with its other files without syncing the directory. A retirement that later found the binding gone synced only the directories that still existed. If the sandbox's parent had been removed and created again, that sync missed the directory that held the old entry, so a crash of the host could bring the binding back after the retirement had deleted the generation it names. A removal now removes the binding first and syncs the sandbox's directory before anything else goes. The configuration record still goes last, so a failed sync leaves a provisioned sandbox that can be removed again. The sync also covers a release that a crash interrupted before its own sync. This costs one more directory sync per removal on Unix; on Windows syncing a directory is a no-op. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
…rder A clone's lease now reserves the launch of its clone. The acquisition draws the launch's identity, records it in the lease's record and in the clone binding before any VMM of the clone exists, and returns it as Lease::launch. The supervisor takes the launch's identity from its caller: supervisor::launch and LockedStore::begin_launch take a LaunchId, which callers that launch no clone draw with LaunchId::generate, now public. A lease allows one launch of its clone, under the lock that acquired it: begin_launch refuses, with sandbox-leased, any other launch of a sandbox that holds a lease, a launch under another lock, and a second launch, until a release ends the lease. After a crash before the launch, a release ends the lease at once, and another acquisition checks the generation's eligibility again. A stop or a termination clears the runtime record only if it is still the record of its own VM, and not another VMM's under a launch identity that its caller used again. The supervisor's documentation describes the launch of a clone, its crashes, and one lock order: the template store's lock, then the sandbox's lifecycle lock, which guards its clone binding and launch journal, then the scratch image's claim. No lock lasts for a VM's lifetime. Tests crash a clone's orchestrator, in a process of its own, at each step of an acquisition and of a release, and around the spawn of the clone's VMM, and check what each evidence of a VM's exit means for its lease: an earlier boot, a reused process ID, a process that cannot be examined, and a forced teardown that fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
Four lease tests began a launch, dropped it, and asserted that the next recovery ends it. On Linux, a process that a concurrent test forks holds a copy of the closed log until it executes its program. If that outlasts recovery's own wait, the recovery is indeterminate and the test fails although nothing is wrong. The tests now retry the recovery until it ends the launch, through test_support::released, as the sandbox store's tests do. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
A template is a capture of a microVM that consumed only its invariant configuration, for clones that each bind their own image, upper, and export when they are restored. Before a store captures one, the host needs the exact VM and options of that capture: OpenVMM refuses a platform-tier capture whose kernel command line holds a token outside its platform allowlist, and a template must hold no tenant data. Add nvx::host::template::TemplatePlan, which lowers an adapter's SandboxPlan without I/O into: - the placeholder distro, bound through its DistroBinding, whose length is the capacity of every clone's image, at an absolute path that OpenVMM's block device options hold whole, and that it attaches as a raw image, over a dormant scratch device, a sparse file in the capture's transaction directory; - the dormant filesystem slot, and no NIC or one fixed NIC under the template's policy, which may allow neither ingress nor host-loopback access both ways, since OpenVMM publishes the guest's listeners through them as live host attachments, which no snapshot holds; - TemplateResources: every processor online at boot, and RAM with a --memory-capacity, in blocks of 128 MiB; - the kernel command line nvx_sandbox=1, the one token that the host adds to those that OpenVMM writes itself; - the capture's options: --snapshot-tier platform, a --snapshot-destination in the transaction directory, under the name of a generation's snapshot in the template store, and --snapshot-block-identity generation with the capture's StorageGeneration, the bound-block storage generation, so that the capture hashes no block device. The tests check each token of a template's command line with OpenVMM's own allowlist, compiled from verbatim excerpts of its source in tests/vectors/openvmm/, which the ignored test of tests/openvmm_vectors.rs compares with the OpenVMM submodule. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 32eea669-a52c-4a53-87fa-521c498537e5
Enrique Saurez (esaurez)
changed the base branch from
esaurez/839-pr3-leases-20261024
to
esaurez/839-pr4-supervisor-seam-20261024
October 25, 2026 00:59
Enrique Saurez (esaurez)
force-pushed
the
esaurez/841-pr1-template-lowering-20261024
branch
from
October 25, 2026 00:59
7f55573 to
290a45b
Compare
This was referenced Oct 25, 2026
Enrique Saurez (esaurez)
force-pushed
the
esaurez/839-pr4-supervisor-seam-20261024
branch
2 times, most recently
from
October 27, 2026 04:39
b7ac52f to
061e368
Compare
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 #886; this PR retargets when its parent merges.
Chain: #871 → #879 → #881 → #885 → #886 → this PR.
Part of #841 (PR 1 of 3: template lowering).
What and why
A template is a capture of a microVM at OpenVMM's platform tier, after the VM consumed only its invariant configuration, for clones that each bind their own image, upper, and export when they are restored. Before a store can capture one, the host needs the exact VM and options of that capture, and they must meet OpenVMM's rules for the platform tier: a kernel command line of allowlisted tokens only, no tenant data, and nothing that a snapshot cannot hold.
nvx::host::template::TemplatePlan::newlowers an adapter'sSandboxPlan(SandboxPlan::adapter) into that plan, without I/O:distro, bound through itsDistroBinding: the plan's image must be the format and identity that the binding names, and the length that the binding records is the capacity of every clone's image (image_capacity()). Its path must be absolute, since OpenVMM would resolve a relative one against the directory in which it runs; it may hold no comma or semicolon, which OpenVMM's block device options would split; and its name may not end in.vhd,.vhdx,.vmgs, or.iso, which OpenVMM would not attach as a raw image, as a sandbox's validation andPreparedLaunchcheck them.SCRATCH_NAME(scratch.raw) in the capture's transaction directory, a sparse file whose length (scratch_length()) and block sizes the device takes, and which the template does not keep.TemplateResources: 1, 2, 4, or 8 processors, all online at boot (B = C), and RAM with--memory-capacity, both in blocks of 128 MiB (MEMORY_BLOCK_SIZE). The capacity is always rendered: without it, a capture records no memory expansion, andTemplateManifest::readrefuses it.nvx_sandbox=1(kernel_command_line()), which the caller passes as--cmdlinealone, in place of the adapter plan's, which names the root's image identity. OpenVMM writes the other tokens itself, each on its platform allowlist: the console, reboot, and panic settings,nr_cpus=,nvx_snapshot_tier=platform, the virtio devices and the control tty, and the NIC's addresses.--snapshot-tier platform,--snapshot-destinationatSNAPSHOT_NAME(snapshot, the name by which a generation of the template store names its snapshot) in the transaction directory, and--snapshot-block-identity generationwith the capture'sStorageGenerationas--snapshot-generation-id, so that the capture hashes no block device. The template keeps no storage generation: OpenVMM unbinds itsdistro, and its scratch is fresh.openvmm_arguments()renders the devices by path, asSandboxPlan::openvmm_argumentsdoes, thencapture_arguments(), which a caller that renders the devices' arguments itself can take alone.The bound-block storage generation is #881's
StorageGeneration, rendered with itsDisplay; this PR adds no type of its own for it, and derives it from none of the other namespaces of a template's generations.Outside
nvx-rs/src/host/template/plan.rsand its tests, the change declares and re-exports the module, makessandbox::reject_disk_path,sandbox::reject_container_path, andSandboxDistro::pure_pathcrate-visible, extends the OpenVMM vector test, and updates the docs.Decisions to review
nvx_config=0xd0010000,65536is on OpenVMM's allowlist, but nothing in NVX defines or reads it, so a template does not pass it. Passing it later changes one constant.--snapshot-destination, OpenVMM opens each block device itself, without following a link at its path (O_NOFOLLOW;FILE_FLAG_OPEN_REPARSE_POINT), and on Windows without sharing write access. APreparedLaunch's/proc/self/fd/Npaths, and on Windows its inherited scratch claim, therefore don't serve a capture at this OpenVMM revision, and the docs here name no such composition. PR 2 therefore hands OpenVMM the paths of the files that it checked, and holds no writable handle on them when OpenVMM starts.Tests
nr_cpus=,nvx_snapshot_tier=,virtnet_ip=,virtnet_gw=), are checked with OpenVMM's own allowlist function, compiled from verbatim excerpts of its source innvx-rs/tests/vectors/openvmm/. The ignored test oftests/openvmm_vectors.rs, which the NVX CLI jobs run, checks that the excerpts are whole lines of the submodule's files.Validation
No VM is needed for this change: everything below ran without one.
nvx-rs, withNVX_BUNDLE=skip:cargo fmt --all --check;cargo clippy --locked --all-targets --all-features -- -D warnings;cargo clippy --locked --lib --no-default-features -- -D warnings;cargo clippy --locked --all-targets --no-default-features --features <feature> -- -D warningsforhost,cli,testing,serde, andbundled;cargo test --locked --all-features;cargo test --locked;cargo build --locked --lib --no-default-featuresandcargo test --locked --lib --no-default-features, also with--features host;RUSTDOCFLAGS="-D warnings" cargo doc --locked --no-deps --all-features;cargo +1.89 check --locked --all-targets --all-features; and, from the root, the ignoredcargo test --locked --manifest-path nvx-rs/Cargo.toml --no-default-features --test openvmm_vectors -- --ignored. Then the commands ofcheck-aci-edge-sandboxesforaci_edge_sandboxes, those ofcheck-nvx-rsfortests/runtime-adapter(protocol-only and host-only), andpython scripts/check_markdown_links.py.--features guest,cargo check --target aarch64-apple-darwinfornvx-rsandaci_edge_sandboxes,python3 scripts/nvx.py check-guest-binaries, and the runtime adapter's static musl guest configuration.dev; a stacked PR runs none.