Skip to content

nvx-rs: define the activation record of a layered root and its limits - #871

Merged
Pedro Henrique Penna (ppenna) merged 3 commits into
devfrom
esaurez/838-pr1-activation-record-types-20261024
Oct 27, 2026
Merged

Pedro Henrique Penna (ppenna) merged 3 commits into
devfrom
esaurez/838-pr1-activation-record-types-20261024

Conversation

@esaurez

@esaurez Enrique Saurez (esaurez) commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #876; this PR retargets to dev when the pull requests below it merge. The chain on dev is #866 → #882 → #883 → #876 → #871 → #873 → #874, after #863, #864, #865, #867, #868, #869, #870, #872, #875, and #878, which have merged.

A clone that the host restores from a template keeps the template's captured configuration, so nothing tells its guest the image, upper, export, network, and workload view of its own launch, and nothing identifies the settings that a guest applied. This adds nvx::activation, the record of those settings and its limits, without features, beside nvx::protocol, so that the host, the guest, and every adapter's framing check it by the same rules. It changes no behavior: nothing builds, carries, or applies the record yet.

Part of #838: the first of its three pull requests. The second, #873, encodes the record canonically, and the third, #874, adds the encoding of Ready's activation and the conformance vectors, and removes the configuration identity, so that Ready reports only that the activation applied, and a clone's transaction ID.

What changed

The record (nvx-rs/src/activation/mod.rs). An ActivationRecord holds the schema's VERSION, a clone's RestoreSection, and the Configuration:

  • the Image: EROFS or GPT, the Identity of the image's binding, the EROFS UUID or GPT disk GUID that the guest expects, if any, and a GPT disk's first-partition bound;
  • the Upper: a tmpfs, or the scratch, with its binding's Identity and its Journal policy;
  • the OverlayMode;
  • an Export, a directory or an aggregate, with its plan's Identity, which covers the export's whole shape, and its 1 to 4,096 mappings;
  • the Network: its policy's Identity, an optional DNS address, the listeners' Publication, in the vocabulary of nvx::guest::entry, and an optional proxy endpoint;
  • the WorkloadView: the hostname, the MachineIdMode, the image's own machine ID or a generated one, and an optional WorkloadIdentity, a UID and a GID, neither 0 nor u32::MAX;
  • the ExpectedResources: the CPU count and the memory size that the guest must have.

A clone's RestoreSection holds the restore's 16-byte TransactionId and the TemplateReference: the template's StoreGeneration, the nonzero publication ID that its store assigns in the order of publication and never reuses, and the Identity of the template's key. An Identity is 32 bytes that the host computes, such as the SHA-256 digest of an object's canonical form; the record never holds the objects that identities name. ReadyActivation is what Ready reports: the ConfigurationIdentity, which #874 removes so that nothing hashes the record, and a clone's transaction ID.

The other inputs of the root's assembly are invariants of the template, as the module documents: the runtime directory, the block devices' MMIO windows and sizes, the GPT partitions that the guest expects, of which there are none, and how long the guest waits for its devices. Secrets, per-execution environments, and host mapping tables never enter the record.

The limits are public constants: MAX_RECORD_BYTES (1,024), MAX_MAPPINGS (4,096, protocol::MAX_HOST_MAPPINGS), MAX_HOSTNAME_BYTES (64), and MAX_PROXY_ENDPOINT_BYTES (255). ActivationRecord::new admits a configuration only once Configuration::check finds that it keeps them, and otherwise reports the first one broken, in the order of the fields. Besides each limit, an export without mappings, mappings without an export, and a zero UID or GID, it refuses:

  • a UID or GID of u32::MAX, which the kernel reads as an unchanged ID, and which the guest's entry and the managed agent refuse;
  • a hostname with a NUL, as the guest's entry does;
  • a proxy endpoint with anything other than visible ASCII characters, or with an @, which would start credentials;
  • a zero CPU count, memory size, or first-partition bound, which no guest matches.

Documentation. The module documentation describes the configuration, the template's invariants, and the limits, with an example. The crate's README and doc/project-structure.md list the module.

Tests (nvx-rs/src/activation/tests.rs): each limit at and past its edge; an export without mappings and mappings without an export, refused; a UID or GID of 0 or u32::MAX, refused, each alone; a configuration with every part at its limit, admitted; cold-boot and clone records; the order in which limits are reported; the errors' messages; the opaque values' bytes and hexadecimal forms; and store generations, nonzero numbers that compare in the order of publication and display in decimal.

Validation

At 3032e6d, the head, rebased onto #876 without changes to the contents of its commits, on Windows and on Linux x86_64:

  • cargo fmt --check; cargo clippy with --all-targets --all-features, --lib, and --lib --no-default-features, -D warnings; cargo test with all, default, and no features; cargo doc with -D warnings; cargo +1.89 check;
  • the runtime adapter's protocol-only and host-only configurations, and, on Linux, its guest-only configuration;
  • aci_edge_sandboxes' checks;
  • on Linux, also the bundled artifacts test, the macOS check of both crates, the guest binaries' lint, tests, build, and check, and the Markdown link check.

At each of the three commits, cargo clippy with --all-targets --all-features, --lib, --lib --no-default-features, and --lib --no-default-features --features guest, -D warnings, passed on both systems.

Before the rebase, on dev, the same checks passed at the first and the third commits, and at the heads of #873 and #874 that contained the second, which answers the review, and CI passed at the third, in run 40121956945. At the first commit, one test outside this change failed once on Linux, in the all-features run on a busy host: guest::cgroup::tests::settlement_collects_statuses_between_its_checks_until_the_deadline, whose collections.get() > 1 assertion races a 50 ms deadline. At the same head, it passed 50 of 50 runs alone, and the all-features suite passed 3 of 3 runs. While this pull request targets #876's branch, dev's CI doesn't run on it.

The third commit makes the StoreGeneration the nonzero number that the template store of #839 assigns to each publication, in place of 16 opaque bytes, so that a record names a template's generation in the store's own terms.

The guide sections that #838 names follow the numbering of #591, which isn't on dev, so the record is documented in the module and the crate's README.

Copilot AI balanced review requested due to automatic review settings October 24, 2026 17:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Workload identity validation accepts u32::MAX, which the existing guest entry rejects.

1 open issue
What changed in this PR

Introduces feature-independent nvx::activation types and limits for layered-root configuration, the first step of #838. Encoding, identity calculation, transport, and application remain outside this PR.

Changes:

  • Defines activation records, restore references, configuration settings, and Ready metadata.
  • Adds validation and tests for limits, invalid combinations, and opaque identifiers.
  • Documents the module in the crate and repository guides.
File Description
nvx-rs/​src/​lib.rs Exposes and documents the activation module without feature gates.
nvx-rs/​src/​activation/​tests.rs Tests boundaries, validation errors, record variants, and identifiers.
nvx-rs/​src/​activation/​mod.rs Defines activation types, public limits, and validation.
nvx-rs/​README.md Documents the activation API and feature-independent availability.
doc/​project-structure.md Adds activation to the repository layout.

Review effort: Balanced

Comment thread nvx-rs/src/activation/mod.rs Resolved
Copilot AI balanced review requested due to automatic review settings October 24, 2026 18:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The additive data model has focused validation and boundary coverage, with no unresolved findings or runtime behavior changes.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The additive contract and boundary tests have no unresolved findings, and the prior workload-ID validation issue is fixed.

No open issues 🎉

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The additive model matches the stated scope, has thorough boundary tests, and has no unresolved blocking findings.

No open issues 🎉

Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) force-pushed the esaurez/860-pr1-resolver-runtime-binds-20261024 branch from 50f7fe1 to f3e0317 Compare October 27, 2026 05:05
Base automatically changed from esaurez/860-pr1-resolver-runtime-binds-20261024 to dev October 27, 2026 13:03
Enrique Saurez and others added 3 commits October 27, 2026 06:17
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
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the esaurez/838-pr1-activation-record-types-20261024 branch from 3032e6d to 0e11a65 Compare October 27, 2026 13:17
Copilot AI balanced review requested due to automatic review settings October 27, 2026 13:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The additive types and validation match the stated scope, cover boundary conditions, and resolve the prior workload-ID issue without changing runtime behavior.

No open issues 🎉

Review effort: Balanced

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants