Repository navigation
nvx-rs: a canonical activation record for platform clones #838
Description
Parent: #834 (#833), step 1. Needs: nothing. Unblocks: #843, #844, and the Ready activation that #821 reports.
Goal. One bounded, versioned, non-secret record that tells a restored clone everything it needs to activate, and one encoding of what a guest reports in Ready about its activation: that it applied, 0 for a cold boot, or 1 and the transaction ID for a clone. Hosts, guests and every adapter's framing share the same codec. No VM is involved.
Today (dev d04d9fb):
- No activation record exists. A restored guest keeps its captured configuration and binds (
guest/common/nvx-snapshot). - A cold root's settings are
nvx::guest::root::Assembly { runtime, distro, image, upper, overlay, timeout }(nvx-rs/src/guest/root.rs:283-400). The workload view isnvx::guest::entry::Identity,Entry::hostnameandMachineId(nvx-rs/src/guest/entry.rs:345-429,:538-590). - A layered sandbox runs every execution as one nonzero UID and GID, whose account the image must hold, with no supplementary groups or capabilities (
nvx-rs/src/protocol/features.rs:24-25,nvx-rs/src/guest/managed_agent/mod.rs:222-236). The host passes them to OpenVMM, which writesnvx_workload_uidandnvx_workload_gidon the command line (nvx-rs/src/host/sandbox/plan.rs:616-619). A platform snapshot refuses those tokens (openvmm/openvmm_helpers/src/snapshot/microvm.rs:2326-2350), so a clone gets its workload identity from the record. - The modules that compile without features are
contract,exec,gpt,net,protocolanduuid(nvx-rs/src/lib.rs:119-142). Thehostfeature enablessha2; theguestfeature doesn't (nvx-rs/Cargo.toml).
Stacked PRs. Each PR builds on the one before it, and lands with its own tests.
PR 1 of 3: the record's types and limits.
- A new module,
nvx::activation, compiled without features besidenvx::protocol. Configuration: the non-secret settings that a root activation applies:- image: its format, the image binding's identity, and an optional expected EROFS UUID or GPT disk GUID;
- upper: tmpfs, or the scratch with the scratch binding's identity;
- export: none, or one export, a directory or an aggregate, with the export plan's identity and the mapping count, from 1 to 4,096. The identity covers the export's shape, so aggregate exports (nvx-rs: aggregate exports for clones #857) don't change the record;
- network: the policy's identity, an optional DNS address, the listener publication mode, and the proxy endpoint without credentials;
- workload view: the hostname, the machine-ID mode (generated or the image's own), and an optional workload identity: a nonzero UID and GID, whose account the image must hold, with no supplementary groups or capabilities, as a layered sandbox's identity is today. An adapter whose executions name their own identity, as CRI containers do, leaves it out;
- root settings: every other
Assemblyinput (the overlay mode, the journal policy, the first-partition bound), each either a field or a declared template invariant; - expected resources: the CPU count and the memory size that repair must have produced.
ActivationRecord: the schema version, an optional restore section, and theConfiguration. A clone's restore section holds the restore transaction ID (16 random bytes that the host picks per restore, and keeps across retries) and the template reference (the store generation and the key's identity). A cold boot's record (nvx-rs: move NVX's adapter to NVXC v2 on the shared engine, with a validated Ready #821) has no restore section.ReadyActivation: what Ready reports about the activation: that it applied, for every layered root, and the transaction ID, for a clone only.- The limits are public constants: the record's size (1,024 bytes), the mapping count, the hostname (64 bytes) and the proxy endpoint. Constructors check every limit, the pairing of an export with its mappings, and that a workload identity's UID and GID are nonzero.
- Tests: each limit at and past its edge; an export without mappings, and mappings without an export, refused; a zero UID or GID, refused.
PR 2 of 3: the canonical encoding.
- An encoder and a decoder with a fixed field order, explicit tags and lengths, and no padding. A record is at most 1,024 bytes, so that the restore packet could carry the same bytes later (the performance workstream).
- Decoding refuses unknown versions, unknown, duplicate or conflicting fields, non-canonical forms, anything over a limit, an export without mappings, and mappings without an export. The encoder refuses anything that the decoder would.
- Tests: round trips of generated records; each refusal; a maximal record within 1,024 bytes.
PR 3 of 3: Ready's activation, and the vectors.
- No configuration identity: nothing hashes the record. A clone's transaction ID ties its Ready to its record, since the host picks a new one for each restore and sends no other record under it. The
ConfigurationIdentitythat PR 1 defined goes. ReadyActivation's canonical encoding:0for a cold boot, or1and a clone's 16-byte transaction ID. NVXC v2's Ready (nvx-rs: move NVX's adapter to NVXC v2 on the shared engine, with a validated Ready #821) and the test adapter's Ready (nvx-rs: the restricted control state and Activate after a restore, in the guest library and the test adapter's framing #843) embed these bytes, so a clone's Ready has the same encoding as a cold boot's.- The
guestfeature doesn't gainsha2, and the guest binaries keep their bytes. - Canonical vectors, checked in, in nvx-rs: turn the wire-format golden bytes into versioned conformance vectors #727's vector format: records, and what Ready reports about their activations.
- Tests: the vectors; cold-boot records without a restore section, and clone records with one;
ReadyActivationwithout a transaction, and with one.
Not in scope: carrying the record (#843 in the test adapter's framing, #851 in NVXC v2); applying it (#843, #845); building it on the host (#844); carrying it without RPC (#544, the performance workstream).
Guide (#591's numbering): 5.5.9 (the record replaces restore packet v5's activation data), 5.5.5 (the restore packet stays v4, for repair only), 4.4.2 (Ready's activation), 1.5.1 (the activation contract's version).
Done when: the three PRs are merged, and #821's Ready can embed ReadyActivation.
Activity
nvx-rs: define the activation record of a layered root and its limits
nvx-rs: encode activation records canonically
nvx-rs: carry the store generation as a nonzero publication ID
nvx-rs: identify activation configurations and encode Ready's activation
nvx-rs: define the activation record of a layered root and its limits
nvx-rs: carry the store generation as a nonzero publication ID
nvx-rs: encode activation records canonically
nvx-rs: identify activation configurations and encode Ready's activation
nvx-rs: report an activation in Ready without a configuration identity
[-]nvx-rs: a canonical activation record and configuration identity for platform clones[/-][+]nvx-rs: a canonical activation record for platform clones[/+]nvx-rs: define the activation record of a layered root and its limits
nvx-rs: carry the store generation as a nonzero publication ID
nvx-rs: encode activation records canonically
nvx-rs: identify activation configurations and encode Ready's activation
nvx-rs: report an activation in Ready without a configuration identity