Skip to content

SL2.4a1: the root's kernel tokens carry what VmConfig sets, and the guest caps a RAM upper - #1278

Merged
Pedro Henrique Penna (ppenna) merged 3 commits into
feature/nvx-refactorfrom
esaurez/slim-2.1.2-sl2.4a1-20261103
Nov 7, 2026
Merged

Pedro Henrique Penna (ppenna) merged 3 commits into
feature/nvx-refactorfrom
esaurez/slim-2.1.2-sl2.4a1-20261103

Conversation

@esaurez

@esaurez Enrique Saurez (esaurez) commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Part of #1098: SL2.4a1, the root's kernel tokens carry what VmConfig sets, and the guest caps a RAM upper.

Stacked on #1269 (SL2.3a, in #1097), the configuration parent that #1098 names. The source stack includes 256ebe3dd9c23f20da94503b55d678d1193e392d; its eventual integration destination is feature/nvx-refactor.

What and why

A layered root's boot tokens named only its image and its upper, so a guest couldn't reproduce the overlay mode, the journal policy, the GPT bound or the RAM upper's cap that a VmConfig sets. This pull request extends the existing protocol::root codec and its shared fixture with these four settings, and the guest mounts a capped RAM upper with size=N%.

  • RootLayout gains four optional settings: overlay and journal, of the activation's types OverlayMode and Journal; gpt_first_partition_bound; and max_memory_percent. Each renders one token after the upper's, in this order: nvx_overlay_mode=trusted-lowers or untrusted-lowers, nvx_scratch_journal=allowed or forbidden, nvx_first_partition_bound=N, from 1 to 18446744073709551615, and nvx_ram_upper_percent=N, from 1 to 90. A layout without settings renders the same two tokens as before, so no producer's command line changes. RootLayout::tokens returns a Vec<String> instead of a [String; 2].
  • RootLayout::parse stays strict and probes nothing. It refuses a setting without a value or repeated, a value that it doesn't know, a number with a sign or leading zeros or out of its range, a journal policy without a scratch upper, a cap without a RAM upper, a bound without a GPT image, and settings without an image, and its messages say what it refuses.
  • guest::root's Upper::Tmpfs gains max_memory_percent: Option<u8>. The assembly refuses a cap outside 1 to 90 with the new Error::TmpfsCap before any system call, and adds size=N% to a capped tmpfs's options; an uncapped one mounts as before.
  • The sandbox guests' init agent, guest/common/nvx-init-agent, reads the four tokens by the same rules and with the same messages as nvx-rs, and mounts a capped RAM upper with size=N%. Before it mounts the image, the upper or the overlay, it refuses what it can't assemble: untrusted lowers, since it mounts the overlay only for trusted lowers, and a forbidden journal, since it reads nothing of the scratch's superblock. It still refuses a GPT image, and so every bound, after checking the bound's form and range.
  • The callers keep their behavior. The sandbox plan's root names no settings. The activation maps the record's tmpfs upper to Upper::Tmpfs without a cap, so the activation record, still version 1, and restores mean what they meant. The runtime adapter's worker passes the cap that it parses to its assembly; its plans name no settings and, as before, refuse a command line whose root differs from theirs.
  • BOOT_CONTRACT_VERSION stays 5, as nvx-rs slimming 2.1.2: the kernel command line and OpenVMM's arguments come from the configuration alone #1098 says, since no released build renders these tokens. MICROVM_ABI_VERSION, the control contract's revision and the activation record's version don't change either.
  • nvx-rs/README.md, the guest file-system design and the runtime adapter's README describe the settings, their defaults and what the init agent refuses.

As #1098 says, this pull request doesn't render the whole kernel command line from VmConfig (SL2.4a2) or carry the cap in the activation record (SL2.5b), and adds no image format, upper kind, overlay mode, journal policy or fallback. protocol::root's crate-private RAM_UPPER_CAPS repeats the range of 1 to 90 that host::config keeps privately, since the protocol module builds without the host's code.

Public API

python scripts/public_api.py --update adds 8 lines to both snapshots, and 3 more to Linux's; it removes none:

  • protocol::root's token constants OVERLAY_MODE_TOKEN, SCRATCH_JOURNAL_TOKEN, FIRST_PARTITION_BOUND_TOKEN and RAM_UPPER_PERCENT_TOKEN, and RootLayout's fields overlay, journal, gpt_first_partition_bound and max_memory_percent.
  • On Linux, the hidden internal guest::root::Upper::Tmpfs's field max_memory_percent, and its hidden Error::TmpfsCap with the field percent.

The snapshots list names, not signatures: code that builds a RootLayout without the new fields, uses Upper::Tmpfs without braces, or keeps RootLayout::tokens in an array no longer compiles.

Size

222 core lines in 5 counted files, as measured; #1098 estimated 120 in 3, with a stop at 240. nvx-rs/src/protocol/root.rs counts 138, nvx-rs/src/guest/root.rs 31, guest/common/nvx-init-agent 46, nvx-rs/src/host/sandbox/plan.rs 4 and nvx-rs/src/guest/clone/activation/apply.rs 3. Tests add 534 lines, the documentation and the API snapshots 66, and the fixture 41. The runtime adapter's src/worker.rs counts as a test file, but its 8 added lines are built into the adapter's guest agent, which the adapter's VMs run.

Before the first commit, a draft measured 244 core lines, past the stop. Instead of stopping to ask, as #1098 requires, it was cut to 202, with the same behavior and tests: one list of the parameters seen serves both the repetition check and the message for settings without an image, the placement messages are shorter, numbers go through str::parse behind a check for a sign or leading zeros, and the shell's case arms take one line each. The one-per-line shell initializers account for 3 further lines; that lint correction is folded into the single logical commit.

The unsigned-bound correction adds 17 core lines to that 205-line implementation, for the current 222; it adds no counted file.

Tests

This layer retires or relocates no tests and adds 3 tests and 4 helpers. python scripts/check_test_changes.py --base 7afa20575c01be01f3cdf148c90d9d9f6c9bed66 --head ba9b85f8eb1fe65dd699ceaf076177ab9ed6c9ed reports 43 changes to existing test code and data, 0 relocated unchanged, and 7 added entries. The rebase includes the target's existing bounded adversarial fixture fixes, unchanged; they are no longer modifications in this pull request. tests-modified remains set. The root settings, GPT validation-order correction and exact missing-image fixture expectations are retained.

The six tests that #1098 names are rewritten, and keep every old case:

  • protocol::root's wire_values_keep_their_literals keeps the old literals, and pins the new tokens', their values and the cap's range.
  • every_layout_renders_its_tokens_and_reads_them_back renders 144 layouts instead of 8: each old one with each setting that its image and upper take, absent or at its bounds. It reads each back from its tokens in order, reversed, and among other tokens.
  • every_reader_parses_the_recorded_command_lines_alike reads every recorded case, old and new, as the exact layout or message that the fixture records, and checks that the cases cover each format, upper, overlay mode, journal policy and setting.
  • the_boot_contract_version_names_these_tokens still pins (BOOT_CONTRACT_VERSION, MICROVM_ABI_VERSION) at (5, 1) and each image's and upper's tokens, and now each setting's token in its place.
  • guest::root's both_overlay_modes_assemble_on_both_uppers_with_their_own_option_sets keeps the scratch and uncapped tmpfs cases, with all their assertions on the mounts and the teardown, and adds caps of 1 and 90 percent: 16 assemblies over both formats and both modes.
  • a_tmpfs_upper_needs_no_scratch_device caps its upper at 50 percent and sees size=50% in its options; it still waits for no device, makes no scratch mount point and tears down in the same order.

18 more guest::root tests change only in their upper: each bare Upper::Tmpfs becomes TMPFS, a test constant for Upper::Tmpfs { max_memory_percent: None }, and every input and assertion stays.

The 18 tests
new_upper_and_work_directories_have_mode_0755_whatever_the_umask
a_fresh_upper_s_directories_must_be_missing_or_empty
a_runtime_directory_where_a_file_system_is_mounted_is_refused
a_refused_option_set_fails_without_a_fallback
an_overlay_that_the_kernel_mounts_without_its_mode_in_effect_is_refused_and_unmounted
options_that_the_kernel_does_not_show_have_their_defaults
effective_options_that_cannot_be_read_fail_the_assembly
overlay_options_that_a_mount_cannot_pass_are_refused_before_any_mount
up_to_195_layers_fit_in_the_overlay_s_options_below_run_nvx
a_tmpfs_upper_is_unmounted_last_and_every_failure_reported
a_scratch_s_file_system_is_synced_right_before_its_unmount
a_gpt_disk_that_differs_from_the_expected_identities_is_refused_before_any_mount
a_gpt_disk_without_a_layer_is_refused_before_any_mount
a_layer_that_is_not_ext4_or_whose_journal_needs_a_recovery_is_refused_before_any_mount
a_missing_partition_fails_at_once_before_any_mount
the_first_partition_is_returned_as_opaque_bytes_within_its_bound
a_teardown_unmounts_the_layers_top_first_and_reports_every_failure
a_layer_that_a_failed_unmount_left_keeps_the_runtime_directory_occupied

The other changes that check_test_changes.py reports:

  • The existing shared settings without a root and a cap before a RAM upper without an image fixtures now require the exact shell missing-image messages already recorded for Rust. The shell reports the first upper or setting parameter that needs nvx_layer, preserving token order, invalid-input rejection and the generic refusal for an entirely rootless command line. No fixture case, workload bound or skip is removed or relaxed.
  • The existing shared a bound past 64 bits fixture now requires the numeric shell error when the GPT image token precedes its overflowing bound. The shell validates the settings and format-aware bound placement before its existing GPT refusal, matching the numeric diagnosis in either token order; valid GPT images remain unsupported. No case, timeout or skip is removed or relaxed.
  • protocol::root: parsing_reports_bytes_that_are_not_text adds settings whose values aren't text. The helper layout gives a layout no settings, and recorded_layout reads the four settings that a case names, and refuses a key that it doesn't know.
  • host::sandbox::plan: every_format_and_upper_plans_and_renders and adapter_plans_render_their_root_and_devices_alone expect no settings, and take the two tokens from a Vec. host::sandbox's launch_contract_builds_agent_command_line expects no settings, with the same command line.
  • host::template::plan: openvmms_platform_allowlist_admits_its_tokens_and_refuses_tenant_data refuses the four new tokens from a tenant too.
  • The activation: assembly_inputs_are_taken_exactly_from_the_record and its helper accepted_assembly expect a tmpfs upper without a cap, and all of the parent's assembly and per-execution-identity assertions stay.
  • scripts/test_nvx_tools.py's SandboxRootTokenAgentTests: _read prints the cap too; test_agent_reads_the_boot_tokens_as_nvx_rs_reads_them compares it with the fixture's; test_agent_explains_the_roots_that_it_cannot_assemble adds untrusted lowers and a forbidden journal; and test_agent_mounts_the_upper_that_the_tokens_name adds a tmpfs mounted with size=25%.
  • The runtime adapter: each_class_of_failure_ends_the_clone_with_its_own_status uses a tmpfs upper without a cap. In guest_session, the helper layout names no settings, the_worker_assembles_an_image_over_ram_in_the_overlay_mode_of_its_configuration passes a cap of 25 percent to the assembly, and the_worker_assembles_a_gpt_disk_with_the_first_partition_bound_of_its_configuration still takes its bound from the adapter's configuration. layered_launch's a_layered_launch_plans_its_blocks_and_boot_tokens_with_the_adapters_plan expects no settings.
  • nvx-rs/tests/fixtures/root-tokens.json keeps its 79 cases, of which only the last one's closing comma changes, and adds 39, 118 in all; its description says what the init agent can't assemble.

Added:

  • guest::root's a_tmpfs_upper_refuses_a_cap_that_is_not_1_to_90_percent: caps of 0, 91 and 255 fail before any system call.
  • host::sandbox::plan's adapter_plans_refuse_the_settings_of_their_root_that_they_do_not_name: an adapter plan refuses a command line whose root has settings that the plan doesn't name.
  • SandboxRootTokenAgentTests.test_agent_refuses_the_settings_with_the_messages_of_nvx_rs: 11 refusals of the init agent, each with nvx-rs's message.
  • The helpers TMPFS, in guest::root's tests, and LAYOUT_KEYS, contracted_overlay and contracted_journal, in protocol::root's.

Every other test of these modules is kept as it was: among them, protocol::root's the_longest_tokens_are_those_of_an_identified_image_over_scratch, which still finds the longest image and upper tokens at 70 and 27 bytes, as launches' command-line budgets count them; 44 of guest::root's original 64 tests; the other sandbox-plan and activation tests; and the other shell root-token tests.

The shell also checks the unsigned bound's upper limit before placement: twenty-digit values are compared a digit at a time, never through signed whole-value arithmetic. Four added shared cases require both readers' exact numeric or placement errors for overflow, an extra digit, the maximum and a lower twenty-digit value.

The follow-up at 89d31a7efc883e00a5b6579d991d9a793a2a2060 was separately qualified: the entire eight-test shell reader class and both Rust shared-reader tests passed on Windows and Linux, with the applicable quality checks. All three affected guest images were freshly assembled with current input manifests. Each WHP/MSHV qualification passed the complete three-case missing-image/RAM probe, all 13 RAM-cap probes, six Alpine and three Ubuntu selections, and 156 canonical adapter scenarios across 11 targets. The initial task probe's device-plan refusal was preserved; the complete corrected three-case probe passed with unchanged messages and bounds. Unchanged compiled inputs were certified against their original builds, not relabelled as new builds or executions. The Validation section retains the original recorded qualification. The current total is 234 core lines in the same five counted files, below the 240-line stop.

The current ba9b85f8eb1fe65dd699ceaf076177ab9ed6c9ed carry onto 7afa20575c01be01f3cdf148c90d9d9f6c9bed66 is mechanical: README context and formatting-only fixture conflicts retain both branches' behavior. Current local checks passed: 29 Windows and 38 Linux Rust/adapter command entries, all 15 quality commands, 1818 Windows unit tests (67 platform skips), 1818 Linux unit tests (35 platform skips), and all 84 adversarial cases on each platform, with the existing 11 Windows and zero Linux skips. The original half-second workload bound, real readiness and descendant-exit checks remain unchanged. No new native guest-image/VM matrix or independent pre-push review was required for this simple rebase; the earlier executions above retain their original source and runtime identities. The current total remains 234 core lines in the same five counted files.

Validation

Source head e5f174b39864938f6fe50768ad3205b10128034a, direct parent 885be7ba1896f5dacb5f2fc7fbf53ecb15f7a847.

The parent contains the pinned qualification baseline
256ebe3dd9c23f20da94503b55d678d1193e392d. The retired per-domain-error
foundation is absent; the crate keeps its single Error, Result and
ErrorKind contract.

The complete applicable action commands passed at this head. These are the
runners' own summaries, not just enclosing exit codes. Earlier-head
executions remain separate and are not counted as current-head evidence.

Check Windows Linux
check-nvx-rs All applicable configurations passed, including bundling All applicable configurations passed, including guest binaries, adapter guest configuration, API snapshots and the macOS cross-check
validate-nvx unit suite 1811 tests, OK, 67 platform skips 1811 tests, OK, 35 platform skips
Adversarial module 84 tests, OK, 11 platform skips 84 tests, OK, no skips
Host resolver 4 tests, OK 4 tests, OK
check-quality Not in the CI matrix; six additional native PowerShell syntax checks passed All required commands passed, including 25 Specula tests and six native PowerShell syntax checks
MicroVM correctness WHP: default 50 selections / 56 completed spans, plus six Ubuntu selections MSHV: default 50 selections / 56 completed spans, plus six Ubuntu selections
Canonical runtime adapter 156 scenarios across 11 targets, all passed 156 scenarios across 11 targets, all passed
Direct root-token probe 13 cases passed 13 cases passed

Rust qualification covers 29 Windows and 41 Linux command entries, with
successful target summaries for all 16 complete Cargo test commands. The
selections overlap; nested child summaries do not add distinct coverage.
Bundling runs with NVX_BUNDLE unset. Python CLI checks select the
exact-source release CLI through NVX_RS. Windows uses native CPython
3.14.9 and Git's usr/bin/sh, and the canceled-capture shell test executes
rather than skipping. Linux's complete Python suite runs in private
PID/mount namespaces, retaining the original UID/GID/groups and a shell as
PID1. The pinned ShellCheck and shfmt binaries run natively. Quality
applicability follows .github/workflows/ci.yml: quality is Linux-only;
Rust and CLI checks cover both systems.

All ten Rust guest executables were validated against this head. Alpine
and Ubuntu initramfses and the Ubuntu EROFS layer were freshly assembled
natively from those executables, with matching source provenance. The
actual Alpine archive's init scripts and all ten executables were checked
byte-for-byte or by hash. Only the hash-verified kernel, configuration,
scratch template and native OpenVMM binaries came from the pinned
baseline: OpenVMM 9cec2b89245f41f2126f9a787c5e43a0d84b7154,
kernel 6.18.38 SHA-256
692ac2a9f351aa5a31dcf1810782d82c1a83a0f1fd0b3edcf4fad9c0d94cfe81.
Consumed runtime and guest hashes remained unchanged across both runs.

On each backend, the direct probes measured uncapped tmpfs and caps of
1, 25 and 90 percent using the kernel's page arithmetic. Eight invalid or
unsupported settings, including both unsigned-bound overflows, were
refused before mounting or running the workload; the permitted
scratch/journal combination booted. Public plans still emit no new
settings. Rust's capped assembly path is covered by the syscall tests;
the direct VM probes exercise the init script and kernel.

Initial failed attempts are retained, not counted as passes. A Linux
compilation stopped on a disk emergency; the complete batch was retried
after space became available. Its host-configuration test then encountered
stale root-owned fixtures from an earlier adapter execution. Those owned
generated fixtures were quarantined, and the entire unchanged
host-configuration test command passed. The first Windows adversarial
command failed two short process-startup cases. Focused baseline/head
comparisons also failed and were diagnostic only; the entire unchanged
84-case module subsequently passed standalone after other owned work
finished. No deterministic timing cause, test relaxation or focused-rerun
waiver is claimed.

An independent read-only review of this exact head was clean, including
220 unsigned-bound/error-order probes and all 118 shared fixture cases.

@esaurez Enrique Saurez (esaurez) added the tests-modified Changes existing tests, test data, or test wiring label Nov 4, 2026
@esaurez
Enrique Saurez (esaurez) requested a balanced review from Copilot November 4, 2026 12:09
@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

This pull request changes existing tests

It modifies or removes 43 existing test asset(s), listed below. Review each with care: a change to a test can hide a regression that the test caught. See doc/contribute.md#changes-to-existing-tests.

Test code

Change What Before After
modified helper fn accepted_assembly in guest::clone::activation::apply::tests nvx-rs/src/guest/clone/activation/apply/tests.rs:627 nvx-rs/src/guest/clone/activation/apply/tests.rs:627
modified helper fn layout in protocol::root::tests nvx-rs/src/protocol/root/tests.rs:21 nvx-rs/src/protocol/root/tests.rs:33
modified helper fn recorded_layout in protocol::root::tests nvx-rs/src/protocol/root/tests.rs:30 nvx-rs/src/protocol/root/tests.rs:50
modified helper def SandboxRootTokenAgentTests._read in scripts/test_nvx_tools.py scripts/test_nvx_tools.py:19408 scripts/test_nvx_tools.py:19408
modified helper fn layout in guest_session (tests) tests/runtime-adapter/tests/guest_session.rs:1635 tests/runtime-adapter/tests/guest_session.rs:1635
modified test fn assembly_inputs_are_taken_exactly_from_the_record in guest::clone::activation::apply::tests nvx-rs/src/guest/clone/activation/apply/tests.rs:916 nvx-rs/src/guest/clone/activation/apply/tests.rs:918
modified test fn new_upper_and_work_directories_have_mode_0755_whatever_the_umask in guest::root::tests nvx-rs/src/guest/root/tests.rs:912 nvx-rs/src/guest/root/tests.rs:917
modified test fn a_fresh_upper_s_directories_must_be_missing_or_empty in guest::root::tests nvx-rs/src/guest/root/tests.rs:958 nvx-rs/src/guest/root/tests.rs:963
modified test fn a_runtime_directory_where_a_file_system_is_mounted_is_refused in guest::root::tests nvx-rs/src/guest/root/tests.rs:1169 nvx-rs/src/guest/root/tests.rs:1174
modified test fn both_overlay_modes_assemble_on_both_uppers_with_their_own_option_sets in guest::root::tests nvx-rs/src/guest/root/tests.rs:1587 nvx-rs/src/guest/root/tests.rs:1592
modified test fn a_tmpfs_upper_needs_no_scratch_device in guest::root::tests nvx-rs/src/guest/root/tests.rs:1676 nvx-rs/src/guest/root/tests.rs:1698
modified test fn a_refused_option_set_fails_without_a_fallback in guest::root::tests nvx-rs/src/guest/root/tests.rs:1698 nvx-rs/src/guest/root/tests.rs:1758
modified test fn an_overlay_that_the_kernel_mounts_without_its_mode_in_effect_is_refused_and_unmounted in guest::root::tests nvx-rs/src/guest/root/tests.rs:1752 nvx-rs/src/guest/root/tests.rs:1812
modified test fn options_that_the_kernel_does_not_show_have_their_defaults in guest::root::tests nvx-rs/src/guest/root/tests.rs:1809 nvx-rs/src/guest/root/tests.rs:1869
modified test fn effective_options_that_cannot_be_read_fail_the_assembly in guest::root::tests nvx-rs/src/guest/root/tests.rs:1868 nvx-rs/src/guest/root/tests.rs:1928
modified test fn overlay_options_that_a_mount_cannot_pass_are_refused_before_any_mount in guest::root::tests nvx-rs/src/guest/root/tests.rs:1927 nvx-rs/src/guest/root/tests.rs:1987
modified test fn up_to_195_layers_fit_in_the_overlay_s_options_below_run_nvx in guest::root::tests nvx-rs/src/guest/root/tests.rs:1978 nvx-rs/src/guest/root/tests.rs:2038
modified test fn a_tmpfs_upper_is_unmounted_last_and_every_failure_reported in guest::root::tests nvx-rs/src/guest/root/tests.rs:2165 nvx-rs/src/guest/root/tests.rs:2225
modified test fn a_scratch_s_file_system_is_synced_right_before_its_unmount in guest::root::tests nvx-rs/src/guest/root/tests.rs:2210 nvx-rs/src/guest/root/tests.rs:2270
modified test fn a_gpt_disk_that_differs_from_the_expected_identities_is_refused_before_any_mount in guest::root::tests nvx-rs/src/guest/root/tests.rs:2278 nvx-rs/src/guest/root/tests.rs:2338
modified test fn a_gpt_disk_without_a_layer_is_refused_before_any_mount in guest::root::tests nvx-rs/src/guest/root/tests.rs:2365 nvx-rs/src/guest/root/tests.rs:2425
modified test fn a_layer_that_is_not_ext4_or_whose_journal_needs_a_recovery_is_refused_before_any_mount in guest::root::tests nvx-rs/src/guest/root/tests.rs:2393 nvx-rs/src/guest/root/tests.rs:2453
modified test fn a_missing_partition_fails_at_once_before_any_mount in guest::root::tests nvx-rs/src/guest/root/tests.rs:2470 nvx-rs/src/guest/root/tests.rs:2530
modified test fn the_first_partition_is_returned_as_opaque_bytes_within_its_bound in guest::root::tests nvx-rs/src/guest/root/tests.rs:2735 nvx-rs/src/guest/root/tests.rs:2795
modified test fn a_teardown_unmounts_the_layers_top_first_and_reports_every_failure in guest::root::tests nvx-rs/src/guest/root/tests.rs:2800 nvx-rs/src/guest/root/tests.rs:2860
modified test fn a_layer_that_a_failed_unmount_left_keeps_the_runtime_directory_occupied in guest::root::tests nvx-rs/src/guest/root/tests.rs:2866 nvx-rs/src/guest/root/tests.rs:2926
modified test fn every_format_and_upper_plans_and_renders in host::sandbox::plan::tests nvx-rs/src/host/sandbox/plan/tests.rs:136 nvx-rs/src/host/sandbox/plan/tests.rs:136
modified test fn adapter_plans_render_their_root_and_devices_alone in host::sandbox::plan::tests nvx-rs/src/host/sandbox/plan/tests.rs:1362 nvx-rs/src/host/sandbox/plan/tests.rs:1367
modified test fn launch_contract_builds_agent_command_line in host::sandbox::tests nvx-rs/src/host/sandbox/tests.rs:142 nvx-rs/src/host/sandbox/tests.rs:142
modified test fn openvmms_platform_allowlist_admits_its_tokens_and_refuses_tenant_data in host::template::plan::tests nvx-rs/src/host/template/plan/tests.rs:525 nvx-rs/src/host/template/plan/tests.rs:525
modified test fn wire_values_keep_their_literals in protocol::root::tests nvx-rs/src/protocol/root/tests.rs:45 nvx-rs/src/protocol/root/tests.rs:89
modified test fn every_layout_renders_its_tokens_and_reads_them_back in protocol::root::tests nvx-rs/src/protocol/root/tests.rs:67 nvx-rs/src/protocol/root/tests.rs:128
modified test fn every_reader_parses_the_recorded_command_lines_alike in protocol::root::tests nvx-rs/src/protocol/root/tests.rs:144 nvx-rs/src/protocol/root/tests.rs:277
modified test fn parsing_reports_bytes_that_are_not_text in protocol::root::tests nvx-rs/src/protocol/root/tests.rs:188 nvx-rs/src/protocol/root/tests.rs:348
modified test fn the_boot_contract_version_names_these_tokens in protocol::root::tests nvx-rs/src/protocol/root/tests.rs:231 nvx-rs/src/protocol/root/tests.rs:427
modified test def SandboxRootTokenAgentTests.test_agent_reads_the_boot_tokens_as_nvx_rs_reads_them in scripts/test_nvx_tools.py scripts/test_nvx_tools.py:19434 scripts/test_nvx_tools.py:19435
modified test def SandboxRootTokenAgentTests.test_agent_explains_the_roots_that_it_cannot_assemble in scripts/test_nvx_tools.py scripts/test_nvx_tools.py:19464 scripts/test_nvx_tools.py:19477
modified test def SandboxRootTokenAgentTests.test_agent_mounts_the_upper_that_the_tokens_name in scripts/test_nvx_tools.py scripts/test_nvx_tools.py:19573 scripts/test_nvx_tools.py:19663
modified test fn each_class_of_failure_ends_the_clone_with_its_own_status in worker::activation::tests tests/runtime-adapter/src/worker/activation/tests.rs:90 tests/runtime-adapter/src/worker/activation/tests.rs:90
modified test fn the_worker_assembles_an_image_over_ram_in_the_overlay_mode_of_its_configuration in guest_session (tests) tests/runtime-adapter/tests/guest_session.rs:1817 tests/runtime-adapter/tests/guest_session.rs:1821
modified test fn the_worker_assembles_a_gpt_disk_with_the_first_partition_bound_of_its_configuration in guest_session (tests) tests/runtime-adapter/tests/guest_session.rs:1843 tests/runtime-adapter/tests/guest_session.rs:1849
modified test fn a_layered_launch_plans_its_blocks_and_boot_tokens_with_the_adapters_plan in layered_launch (tests) tests/runtime-adapter/tests/layered_launch.rs:239 tests/runtime-adapter/tests/layered_launch.rs:239

Test data

Change What Before After
modified file nvx-rs/tests/fixtures/root-tokens.json nvx-rs/tests/fixtures/root-tokens.json nvx-rs/tests/fixtures/root-tokens.json

Relocated unchanged: 0; added: 7. Compared the merge base 7afa20575c01 with ba9b85f8eb1f.

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

No active review issues remain for the layered-root configuration and RAM-cap changes.

No open issues 🎉

What changed in this PR

Implements SL2.4a1 of #1098: extends NVX’s layered-root boot protocol to carry configuration settings and lets guests cap RAM-backed uppers. Complete VmConfig command-line rendering and activation-record changes remain deferred.

Changes:

  • Adds optional overlay mode, journal policy, GPT partition bound, and RAM-cap tokens with strict parsing.
  • Applies RAM caps through tmpfs size=N%, while preserving existing defaults and contract versions.
  • Expands shared fixtures and tests, updates callers, and documents the new API and guest limitations.
File Description
tests/​runtime-adapter/​tests/​layered_launch.rs Keeps planned root settings absent.
tests/​runtime-adapter/​tests/​guest_session.rs Exercises forwarding a parsed RAM cap.
tests/​runtime-adapter/​src/​worker/​activation/​tests.rs Updates uncapped tmpfs construction.
tests/​runtime-adapter/​src/​worker.rs Passes the parsed cap to root assembly.
tests/​runtime-adapter/​README.md Explains boot-token and adapter configuration responsibilities.
scripts/​test_nvx_tools.py Tests shell parsing, refusals, and capped mounts.
nvx-rs/​tests/​fixtures/​root-tokens.json Adds shared setting and validation cases.
nvx-rs/​src/​protocol/​root/​tests.rs Expands round-trip, fixture, and contract coverage.
nvx-rs/​src/​protocol/​root.rs Adds optional settings, token rendering, and parsing.
nvx-rs/​src/​host/​template/​plan/​tests.rs Checks rejection of tenant-supplied root settings.
nvx-rs/​src/​host/​sandbox/​tests.rs Preserves existing launch-token expectations.
nvx-rs/​src/​host/​sandbox/​plan/​tests.rs Tests rejection of settings absent from adapter plans.
nvx-rs/​src/​host/​sandbox/​plan.rs Leaves new settings unset in existing plans.
nvx-rs/​src/​guest/​root/​tests.rs Tests cap limits, mount options, and unchanged teardown.
nvx-rs/​src/​guest/​root.rs Validates RAM caps and applies tmpfs size options.
nvx-rs/​src/​guest/​clone/​activation/​apply/​tests.rs Verifies activation records still produce uncapped tmpfs.
nvx-rs/​src/​guest/​clone/​activation/​apply.rs Preserves activation-record behavior without a cap.
nvx-rs/​README.md Documents root settings and capped assembly.
nvx-rs/​public-api/​windows.txt Records new protocol constants and fields.
nvx-rs/​public-api/​linux.txt Records protocol and guest cap API additions.
guest/​common/​nvx-init-agent Reads settings, rejects unsupported modes, and mounts capped tmpfs.
doc/​design/​4.1-guest-filesystem-and-image-model.md Describes extended tokens and guest mount behavior.

Review effort: Balanced

@esaurez

Copy link
Copy Markdown
Contributor Author

Dependency update following the revised #1076/#1097/#1098 contracts: PR #1267's per-domain-error foundation is retired.

This PR keeps its existing root-token and RAM-upper-cap scope. Move it with the revised #1269 configuration stack so the obsolete error commit is not retained through ancestry. Preserve the shared reader/fixture agreement, strict parsing, tmpfs cap and unchanged boot-contract version; no new error facade or unrelated runtime change is requested.

Keep original execution evidence and its head labels. Qualify affected inputs, checks and reviews after the stack update before current-design readiness. No Ready transition or manual CI dispatch is included.

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

The shell reader misreports an overflowing partition bound as requiring a GPT image, so it still needs safe upper-limit validation and an EROFS overflow regression test.

1 open issue

Review effort: Balanced

Comment thread guest/common/nvx-init-agent Resolved
@esaurez

Copy link
Copy Markdown
Contributor Author

The dependency update requested above is addressed in e5f174b: its direct parent is #1269's published 885be7b, the pinned qualification baseline is in its ancestry, and retired ed9c38c is not. The single crate Error/Result/ErrorKind contract, unchanged boot version and original root-token/RAM-cap scope are preserved.

Complete current-head Windows/Linux checks, fresh native guests and the required WHP/MSHV runs passed; their actual summaries and initial failed attempts are recorded in the updated body. Earlier-head evidence remains separate. The PR is still Draft; no Ready transition or manual CI dispatch was made.

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

No active review issues are identified for the layered-root configuration and RAM-cap 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

No active review issues are recorded for the optional root-setting and RAM-cap changes.

No open issues 🎉

Review effort: Balanced

@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/slim-2.1.1-sl2.3a-20261103 branch from 49d74dd to 3bd7e0d Compare November 6, 2026 21:31
Base automatically changed from esaurez/slim-2.1.1-sl2.3a-20261103 to feature/nvx-refactor November 6, 2026 23:36

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

Early GPT rejection skips bound validation for image-first tokens, causing oversized bounds to produce order-dependent diagnostics instead of the required numeric error.

1 open issue

Review effort: Balanced

Comment thread guest/common/nvx-init-agent Resolved

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

The new cases omit explicit shell error expectations, allowing mismatched fatal diagnostics to pass and leaving the settings’ diagnostic parity untested.

1 open issue
1 resolved since last review

Review effort: Balanced

Comment thread nvx-rs/tests/fixtures/root-tokens.json Resolved
Enrique Saurez (esaurez) pushed a commit that referenced this pull request Nov 7, 2026
Carry only scripts/test_adversarial.py from #1278 at 5b7f85d.
Wait for the fixture's actual live child before timing the workload, accept
the existing raw or wrapped timeout, and await Windows fixture teardown.
Keep the 0.5-second workload deadline and descendant-exit assertions.
Production code and skip guards are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e19737a6-412b-4652-b67e-b37837521925
@esaurez
Enrique Saurez (esaurez) requested a balanced review from Copilot November 7, 2026 07:07

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 boot-settings and RAM-upper cap changes have no active review issues.

1 open issue

Review effort: Balanced

@esaurez
Enrique Saurez (esaurez) marked this pull request as ready for review November 7, 2026 07:24
Pedro Henrique Penna (ppenna) pushed a commit that referenced this pull request Nov 7, 2026
Carry only scripts/test_adversarial.py from #1278 at 5b7f85d.
Wait for the fixture's actual live child before timing the workload, accept
the existing raw or wrapped timeout, and await Windows fixture teardown.
Keep the 0.5-second workload deadline and descendant-exit assertions.
Production code and skip guards are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e19737a6-412b-4652-b67e-b37837521925
Enrique Saurez and others added 3 commits November 7, 2026 08:57
…pper

The boot tokens of a layered root gain four optional settings, after the
image's and the upper's: nvx_overlay_mode, trusted-lowers or
untrusted-lowers; nvx_scratch_journal, allowed or forbidden;
nvx_first_partition_bound, a GPT disk's bound in bytes; and
nvx_ram_upper_percent, a RAM upper's cap from 1 to 90 percent of the
VM's memory. RootLayout names each as an Option that renders no token
when it is None, so that the guest keeps its default. RootLayout::parse
reads them strictly: each once, with a value in its decimal or named
form, only beside an image, and the journal policy only with a scratch
upper, the cap with a RAM upper, and the bound with a GPT image. The
shared fixture records the new cases, which nvx-rs and the shell init
both replay.

guest::root's Upper::Tmpfs takes the cap, mounts the tmpfs with size=N%
when it has one, and refuses a cap outside 1 to 90 percent before
anything changes, since the kernel reads 0 as no cap. NVX's shell init
reads the four tokens with the parser's rules and messages, mounts a
capped RAM upper with size=N%, and refuses, before it mounts anything,
untrusted lowers and a forbidden journal, which it cannot assemble, as
it refuses a GPT image.

The sandbox plan names none of the settings, so its command lines stay
as they were, and an adapter's plan refuses a setting that its command
line names, since it reads the root's tokens back. A clone's activation
assembles its tmpfs without a cap, since its record names none, and the
runtime adapter's worker hands the cap of its boot tokens to its root.

BOOT_CONTRACT_VERSION stays 5, since nothing deployed renders the new
tokens. The public API snapshots list the new items.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Read the root settings before the init refuses GPT images, so an invalid
unsigned bound has the same numeric diagnostic in either token order.
Keep GPT unsupported and pin the existing image-first overflow fixture's
shell diagnostic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8
Track the first upper or setting token so a missing image reports the same
parameter as the Rust reader. Pin both existing missing-image fixture
diagnostics without changing their cases or accepting invalid roots.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4e354d65-494d-4fa9-ae9b-4f2e86c3c0f8

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

There are no active review issues for the boot-setting and RAM-cap changes.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) merged commit eb20dbd into feature/nvx-refactor Nov 7, 2026
60 of 61 checks passed
@ppenna
Pedro Henrique Penna (ppenna) deleted the esaurez/slim-2.1.2-sl2.4a1-20261103 branch November 7, 2026 18:12
Pedro Henrique Penna (ppenna) pushed a commit that referenced this pull request Nov 13, 2026
Carry only scripts/test_adversarial.py from #1278 at 5b7f85d.
Wait for the fixture's actual live child before timing the workload, accept
the existing raw or wrapped timeout, and await Windows fixture teardown.
Keep the 0.5-second workload deadline and descendant-exit assertions.
Production code and skip guards are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e19737a6-412b-4652-b67e-b37837521925
Pedro Henrique Penna (ppenna) pushed a commit that referenced this pull request Nov 13, 2026
Carry only scripts/test_adversarial.py from #1278 at 5b7f85d.
Wait for the fixture's actual live child before timing the workload, accept
the existing raw or wrapped timeout, and await Windows fixture teardown.
Keep the 0.5-second workload deadline and descendant-exit assertions.
Production code and skip guards are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e19737a6-412b-4652-b67e-b37837521925
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat-nvx-refactor tests-modified Changes existing tests, test data, or test wiring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants