Skip to content

host: keep the export's pins in the CLI and paths only in ExportPlan (SL6.5b) - #1549

Merged
Pedro Henrique Penna (ppenna) merged 1 commit into
feature/nvx-refactorfrom
agents/slim-sl6-5b-export-paths-only
Nov 12, 2026
Merged

Pedro Henrique Penna (ppenna) merged 1 commit into
feature/nvx-refactorfrom
agents/slim-sl6-5b-export-paths-only

Conversation

@ppenna

@ppenna Pedro Henrique Penna (ppenna) commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Part of #1125 (SL6.5b, "The export holds paths only"). Parent #1088, umbrella #1076.

Based on feature/nvx-refactor at 19b5404dd0d, where SL6.5a2 (#1544) merged. SL6.5b needs SL6.5a2, which made the export planner and its host rechecks private to the CLI; the planner, the verifier and their callers change together here.

What and why

ExportPlan, the public description of a VM's export, combined the host paths that nvx lowers with the file identities that only the CLI's store needs: each child's denied_identities, each bind's identity, and FileIdentity, with check's rules that a recorded plan pins every object that a workload could move. This PR does SL6.5b's Scope in one commit, host: keep the export's pins in the CLI and paths only in ExportPlan (03b7ccb2):

  • The CLI owns PlannedExport { description: ExportPlan, pins: Pins }, crate-private and cli-gated in host::filesystem, beside the planner and the verifier that SL6.5a2 made private there (host's children may not depend on host::cli, and the planner, which host::sandbox::mount calls too, produces it). FileIdentity stays as the pins' crate-private type.
  • The private planner produces the description and the pins together. Pins::denied holds, per child, the identity of each denied path that a workload could move, and Pins::binds that of each bind's object, computed as before; the binds are sorted with their pins.
  • PlannedExport::check runs ExportPlan::check, then the pin rules that moved out of it, with the same messages: a slot per child and per denied path, one per bind, and the identity of each denied or mapped object strictly inside a part of its child that the guest may write. PlannedExport::verify is the old ExportPlan::verify, which reads the identities from the pins.
  • The store persists both fields. config.json keeps the description under export, paths only, where CI's share checks read it, and records the pins beside it under the new export_pins (null without an export), as it records each image beside its binding. Config::new requires the field, and refuses an export without pins or pins without an export as malformed; Config::check, which each start, sandbox run and provision run, checks and verifies the pair before vm_config passes the description to the VM. plan_mounts, ProvisionConfig and check_images carry the PlannedExport; nvx-rs run, which launches its plan once, right after planning it, keeps the description and checks no pins, as before.
  • ExportPlan holds paths only. FileIdentity, ExportChild::denied_identities, Bind::identity and their structural rules leave it and its check, which stays structural and needs no identity data. ExportPlan::{openvmm_arguments, kernel_token}, which only VmConfig's lowering calls, become pub(crate); lowering is unchanged, so OpenVMM's arguments, the nvx_maps token and the guest mapping table are the same.
  • Docs. host::filesystem gains "What the caller keeps stable": the launch race, the writable-part recheck and durable denials, with what NVX's CLI does for each; ExportPlan and check point there. The README's nvx::host::filesystem section and config.json row, doc/run.md's shares section and the store's module docs follow. The module row of host::filesystem keeps its purpose, so it doesn't change.
  • The public API snapshots lose 7 lines on each platform, and gain none: FileIdentity with its two fields, ExportChild::denied_identities, Bind::identity, ExportPlan::kernel_token and ExportPlan::openvmm_arguments.

What the CLI plans, protects and pins doesn't change, nor do the export's wire formats, guest mappings or OpenVMM arguments, BOOT_CONTRACT_VERSION or CONTROL_CONTRACT_REVISION.

State written before this change

The stored form changes: export loses deniedIdentities and identity, and the record gains export_pins. The record's format stays 10; a configuration that an earlier build wrote, with or without shares, has no export_pins, so start refuses it before anything else with error[format-unsupported]: sandbox configuration has no export pins; deprovision the sandbox and provision it again, through the existing check that refuses a record without one of its typed fields, as it refused records before the bindings or the network were recorded. Nothing is migrated: nothing of this workstream is deployed (#1076, "No backward compatibility"), and #1125 excludes a persistence framework. Bumping the format to 11 would refuse the same records, but would also change CI's share checks in run-nvx-microvm-tests.yml and the sandbox-ram-upper scenario, which read format == 10, two more counted files for no change of behavior.

What each command does with such a sandbox:

  • A sandbox that boots cold is deprovisioned and provisioned again: deprovision reads no configuration for it.
  • A restored sandbox, from snapshot restore, keeps the view of its snapshot in its state directory once it has started. deprovision reads its configuration through Config::new to find the snapshot whose lock guards the view's removal (lifecycle/stop.rs), so it refuses an earlier build's restored sandbox with the same message, and leaves its view and state in place. That is how it has refused a format-9 restored sandbox since 533bbb81f8d; a scratch test of deprovision showed both, at this head. Copilot's review thread asked deprovision to read the snapshot's fields alone, independently of the launch schema; that needs no change here: only this unreleased branch's builds since SL3.8a1 write such records, dev's releases (formats 1, 7 and 8) never create the view, so their sandboxes deprovision without reading a record, and nvx-rs slimming workstream: make nvx a library of mechanisms and value types #1076 keeps no backward compatibility. Such a sandbox is removed by hand: stop it, then remove its state directory while no restore of its snapshot runs.
  • A running sandbox that an earlier build started: exec refuses it, with the same message when these artifacts started it. stop ends it all the same: by forced termination after the grace period, with a warning, as for any failed guest shutdown, or as it ends any sandbox of another runtime identity.

Size

Measured against 19b5404dd0d (feature/nvx-refactor, with SL6.5a2), by #1076's rules: the non-blank, non-comment lines that the commit adds or changes in production Rust files.

Core lines Counted files
Estimate (#1125) 250 4
Stop (#1125) 500
Measured 209 7
  • host/filesystem/verify.rs 131, most of it PlannedExport::check, with the pin rules that left ExportPlan::check, and verify's body, moved under impl PlannedExport, host/filesystem/mod.rs 44, host/cli/sandbox/state.rs 20, host/sandbox/mount/aggregate.rs 7, host/cli/sandbox/mod.rs 3, host/cli/sandbox/lifecycle/mod.rs 2 and host/cli/run.rs 2. host/filesystem/policy.rs only gates Writable by cli, which only the planner and PlannedExport::check now use, so it and its 2 lines don't count.
  • 7 counted files, past the estimate's 4 and nvx-rs slimming workstream: make nvx a library of mechanisms and value types #1076's usual 5, within the 6 or 7 that it allows where a split would be harder to follow: the planner's return type reaches its two callers (run.rs and aggregate.rs), and the store's input reaches provision's ProvisionConfig (lifecycle/mod.rs) and its caller (sandbox/mod.rs); nvx-rs slimming 4.3.2: path rules are private to the CLI, and the export holds paths only #1125 has "the planner and verifier ... and their callers change together", and none of them builds alone.
  • 25 files change: +620 and −355 lines, tests, docs and snapshots included.

Old tests of the area

  • Kept, with the same assertions, reading the description and the pins from the PlannedExport: the planner's and verifier's unit tests in host::filesystem (swapped_objects_fail_verification, objects_that_cannot_be_inspected_fail_verification_by_what_the_failure_shows, read_only_binds_that_the_host_cannot_enforce_fail_verification, denied_paths_belong_to_the_directory_that_contains_them, policy_paths_hide_show_and_narrow_writes, the_caller_owner_reaches_openvmm_on_linux_hosts, an_exported_object_that_a_workload_could_move_is_checked_again_before_a_start, among them), the sandbox's planning and launch-validation tests in host::sandbox, and the CLI's tests that plan shares.
  • Rewritten: narrowed_writes_pin_only_what_lies_in_writable_paths checks the pin rules through PlannedExport::check, where they moved, with the same messages, and adds the slot rules (Test changes). ExportPlan::check's own tests, recorded_plans_hold_no_more_policy_than_one_aggregate_accepts and those of host::config and host::vm, build plans without identity data and refuse the same structural errors.
  • Retired: none.

Done when

  • The CLI stores and reloads an export description with its pins: the new records_keep_the_export_with_its_pins_and_starts_refuse_changed_pins (host/cli/sandbox/state/tests.rs) records a planned export through serialize_launch, finds the description under export and the pins under export_pins, reads both back through Config::new, and gets the description back from vm_config; lifecycles_of_every_configuration (tests/cli/lifecycle.rs) checks what provision records for every share configuration, and the KVM and WHP runs below drive the binary.
  • A changed or uninspectable pin is refused before the CLI passes the description to nvx: the same test refuses, at Config::check, which comes before vm_config, a pinned denied directory that moved with a decoy at its path or went (error[other], ... no longer names the host object it named at provision), and one that the host cannot look up (error[io], cannot inspect ...); the verifier's unit tests above keep their cases; and on KVM and WHP, sandbox start refused a decoy before it launched anything (no runtime.json).
  • ExportPlan::check tests pass without identity data and still reject the same structural errors: above.
  • Export lowering produces the same OpenVMM arguments, kernel token and guest mapping table: the lowering code is unchanged but for visibility; the unit tests of host::filesystem and host::config that pin the arguments, nvx_maps and the table, and the dry_run and one_shot tests of tests/cli, which compare the CLI's OpenVMM command lines with an export that they derive on their own, pass unchanged.
  • The public docs state the launch race, writable-part recheck and durable-denial responsibilities: host::filesystem's "What the caller keeps stable", and the README.

Test changes

python scripts/check_test_changes.py --base origin/feature/nvx-refactor lists 38 changes to existing tests and 3 added items, hence tests-modified:

  • Fixtures of ExportPlan without identity data (8): host::config::tests's helper export, mapping_table_is_the_exact_export_projection_counted_by_the_boot_token and configuration_command_lines_keep_the_same_reserve_on_every_host; host::vm::tests's capture_creation_refuses_layered_roots_and_exports_without_creating_outputs and prepared_record_keeps_checked_root_view_export_resources_and_transaction; the restore tests' helper restored_config; recorded_plans_hold_no_more_policy_than_one_aggregate_accepts; and tests/cli/openvmm.rs's helper fixture_export, which builds the fixture's export as host::filesystem documents it, now paths only. Each only drops denied_identities or identity; the assertions don't change. fixture_export was the rebase's one conflict: SL6.5a2's tests commit, fe269acbe9a, gave each movable denied path a zero FileIdentity, which ExportPlan::check then required; this PR drops it with the field.
  • The planner's and verifier's tests in host::filesystem (12): the helper plan returns the description of the new helper planned (added), which returns the PlannedExport, and plan_blockless returns the PlannedExport; denied_paths_belong_to_the_directory_that_contains_them, swapped_objects_fail_verification, objects_that_cannot_be_inspected_fail_verification_by_what_the_failure_shows, read_only_binds_that_the_host_cannot_enforce_fail_verification, policy_paths_hide_show_and_narrow_writes, the_caller_owner_reaches_openvmm_on_linux_hosts and narrowed_writes_pin_only_what_lies_in_writable_paths read the pins or verify the PlannedExport; explicit_targets_place_mappings_at_any_free_guest_path, read_write_mappings_to_which_writes_are_narrowed_stay_visible and denied_mapped_directories_inside_denied_ones_show_their_allowed_paths take the description. narrowed_writes_pin_only_what_lies_in_writable_paths also refuses a record with a missing slot for a denied path, a missing bind slot, or an extra child slot, the rules that keep the verifier from skipping a pin.
  • The CLI's and the sandbox's tests that plan shares (12): host::sandbox::tests's helpers planned, validated and validated_with and their three tests, host::sandbox::mount::tests::shares_are_planned_through_the_export_planner, host::sandbox::options::tests's helper run, host::cli::run::guest_tokens::tests::planned_boot_shares_keep_table_sources_targets_access_and_file_kind, and starts_check_the_kernel_command_line_that_they_launch, with the imports of the two test modules, read the description or verify the PlannedExport, with the same assertions.
  • tests/cli/lifecycle.rs (6, and 1 added): minimal_config expects "export_pins": null, shared_config expects the pins of its export through the new helper unpinned, since no share of these configurations pins an object, and child and the policy and file configurations of configurations lose deniedIdentities, so lifecycles_of_every_configuration checks the new record; malformed_configuration refuses a record without export_pins (format-unsupported, with the advice), an export without pins and pins without an export (io, malformed); start_refuses_recorded_shares_of_its_own_files writes its export with its pins, so that it still reaches the refusal that it names.
  • Added: records_keep_the_export_with_its_pins_and_starts_refuse_changed_pins (host/cli/sandbox/state/tests.rs), and the helpers planned and unpinned.

doc/acceptance-case-matrix.md's row a_start_refuses_an_export_whose_mapped_objects_changed_before_any_vmm_starts also names the new state test; no inventoried test changes, and check_matrix.py passes.

Validation

I ran the check-nvx-rs steps and the test-layout, test-changes and case-matrix checks from this branch's definitions, with Rust 1.95.0, locally on Windows and on nvx-mshv-test (Azure Linux 3, kernel 6.18, linux-mshv, virtual machine), in temporary worktrees, on this head, 03b7ccb22dc. It is 21ce29a1881 rebased onto 19b5404dd0d, and git range-diff shows no change to the production patch, only to fixture_export (Test changes); the same checks had passed on 21ce29a1881.

Check Windows (local) Linux (MSHV, kernel 6.18)
cargo fmt --all --check passed passed
Clippy -D warnings: --all-targets --all-features, --lib, --lib --no-default-features, and serde, cli, testing and guest alone passed passed; guest alone for the guests' musl target, below
Guest Clippy and tests, x86_64-unknown-linux-musl, guest alone (check-guest-binaries), and build-guest-binaries with test-guest-binaries passed
cargo check --all-targets --all-features --target aarch64-apple-darwin passed
cargo test --locked --all-features passed: lib 1065 passed, 18 ignored; cli 113 passed, 2 ignored; every other integration and doc test passed: lib 1768 passed, 59 ignored; cli 123 passed; every other integration and doc test
cargo test --locked, cargo build --locked --lib --no-default-features, cargo test --locked --lib --no-default-features passed (518; 191) passed (520; 191)
RUSTDOCFLAGS=-D warnings cargo doc --locked --no-deps --all-features, and the compile-only examples passed passed
MSRV: cargo +1.89 check --locked --all-targets --all-features passed passed
python scripts/public_api.py passed passed
check_test_layout.py, check_test_changes.py --base origin/feature/nvx-refactor, check_matrix.py <worktree> 99d2022085f passed passed (layout)
  • microVM scenarios on this head, python scripts/nvx.py test-microvm with NVX_AUTO_DOWNLOAD=0, this head's release nvx-rs, and the artifacts of this pull request's CI run 43316146269 on this head: the kernel, the Alpine initramfs, the Ubuntu distro layer and scratch template, and OpenVMM (openvmm-linux-gnu, openvmm-windows-msvc, of the unchanged pin 14dc4116ec7), each checked by SHA-256 against the run's artifacts and OpenVMM's provenance. All 14 passed on KVM (nvx-kvm-test, kernel 6.6) and on WHP (nvx-whp-test): the public sandbox scenarios managed-exec-config, sandbox-gpt-layer, sandbox-lifecycle, sandbox-proxy-only, sandbox-ram-upper, snapshot-core, snapshot-tiers, untiered-network-policy and vm-lifecycle, and the --mount and filesystem-policy scenarios filesystem-shares, filesystem-policy, filesystem-owner, filesystem-comma-paths and denied-filesystem-paths.
  • The CLI, driven on real VMs on KVM and WHP, on this head: sandbox provision of the Ubuntu layer with --mount /workspace,<share>,rw --mount-deny nvx-denied recorded export with paths only and export_pins with the denied directory's identity ({"device": 2065, "index": 29511296} on KVM, {"device": 2595290065, "index": 3377699720794538} on WHP); start, exec /bin/ls -a /workspace (which showed marker and not the denied directory) and stop passed, and on KVM exec /bin/cat /workspace/nvx-denied/secret failed with Permission denied; with the denied directory moved and a decoy at its path, start failed with error[other]: <share>/nvx-denied no longer names the host object it named at provision; deprovision the sandbox and provision it again and left no runtime.json; on KVM, it failed the same way once the directory went, and with the record rewritten as an earlier build wrote it, identities inline and no export_pins, it failed with error[format-unsupported]: sandbox configuration has no export pins; deprovision the sandbox and provision it again; once the directory was back, start and stop passed, and deprovision removed the sandbox.
  • CI: run 43316146269 on this head passed every job that it ran, Required status check among them: the nvx-rs and NVX CLI jobs on Linux and Windows, the guest and OpenVMM builds, the microVM tests on KVM, MSHV and WHP, the three Platform jobs and the performance regression gate. OpenVMM CI of the pinned revision didn't run, since the pin is unchanged.
  • Review: a code-review subagent reviewed the whole diff before the rebase and found no issue; Copilot reviewed this head and raised none.

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

This pull request changes existing tests

It modifies or removes 38 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 restored_config in host::cli::sandbox::lifecycle::start::tests::restore nvx-rs/src/host/cli/sandbox/lifecycle/start/tests/restore.rs:249 nvx-rs/src/host/cli/sandbox/lifecycle/start/tests/restore.rs:249
modified helper fn export in host::config::tests nvx-rs/src/host/config/tests.rs:85 nvx-rs/src/host/config/tests.rs:85
modified helper fn plan in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:21 nvx-rs/src/host/filesystem/tests.rs:54
modified helper fn plan_blockless in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:1849 nvx-rs/src/host/filesystem/tests.rs:1871
modified helper fn run in host::sandbox::options::tests, impl Shares`` nvx-rs/src/host/sandbox/options/tests.rs:970 nvx-rs/src/host/sandbox/options/tests.rs:970
modified helper fn planned in host::sandbox::tests, impl Fixture`` nvx-rs/src/host/sandbox/tests.rs:227 nvx-rs/src/host/sandbox/tests.rs:228
modified helper fn validated in host::sandbox::tests nvx-rs/src/host/sandbox/tests.rs:234 nvx-rs/src/host/sandbox/tests.rs:235
modified helper fn validated_with in host::sandbox::tests nvx-rs/src/host/sandbox/tests.rs:239 nvx-rs/src/host/sandbox/tests.rs:240
modified helper fn minimal_config in cli/lifecycle (tests) nvx-rs/tests/cli/lifecycle.rs:73 nvx-rs/tests/cli/lifecycle.rs:73
modified helper fn shared_config in cli/lifecycle (tests) nvx-rs/tests/cli/lifecycle.rs:90 nvx-rs/tests/cli/lifecycle.rs:93
modified helper fn child in cli/lifecycle (tests) nvx-rs/tests/cli/lifecycle.rs:103 nvx-rs/tests/cli/lifecycle.rs:122
modified helper fn configurations in cli/lifecycle (tests) nvx-rs/tests/cli/lifecycle.rs:287 nvx-rs/tests/cli/lifecycle.rs:305
modified helper fn malformed_configuration in cli/lifecycle (tests) nvx-rs/tests/cli/lifecycle.rs:1286 nvx-rs/tests/cli/lifecycle.rs:1303
modified helper fn fixture_export in cli/openvmm (tests) nvx-rs/tests/cli/openvmm.rs:50 nvx-rs/tests/cli/openvmm.rs:47
modified imports the imports of host::cli::sandbox::lifecycle::start::tests nvx-rs/src/host/cli/sandbox/lifecycle/start/tests.rs:3 nvx-rs/src/host/cli/sandbox/lifecycle/start/tests.rs:3
modified imports the imports of host::sandbox::tests nvx-rs/src/host/sandbox/tests.rs:4 nvx-rs/src/host/sandbox/tests.rs:4
modified test fn planned_boot_shares_keep_table_sources_targets_access_and_file_kind in host::cli::run::guest_tokens::tests nvx-rs/src/host/cli/run/guest_tokens/tests.rs:47 nvx-rs/src/host/cli/run/guest_tokens/tests.rs:47
modified test fn starts_check_the_kernel_command_line_that_they_launch in host::cli::sandbox::lifecycle::start::tests nvx-rs/src/host/cli/sandbox/lifecycle/start/tests.rs:90 nvx-rs/src/host/cli/sandbox/lifecycle/start/tests.rs:90
modified test fn mapping_table_is_the_exact_export_projection_counted_by_the_boot_token in host::config::tests nvx-rs/src/host/config/tests.rs:1034 nvx-rs/src/host/config/tests.rs:1032
modified test fn configuration_command_lines_keep_the_same_reserve_on_every_host in host::config::tests nvx-rs/src/host/config/tests.rs:2065 nvx-rs/src/host/config/tests.rs:2062
modified test fn denied_paths_belong_to_the_directory_that_contains_them in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:620 nvx-rs/src/host/filesystem/tests.rs:627
modified test fn swapped_objects_fail_verification in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:902 nvx-rs/src/host/filesystem/tests.rs:906
modified test fn objects_that_cannot_be_inspected_fail_verification_by_what_the_failure_shows in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:965 nvx-rs/src/host/filesystem/tests.rs:964
modified test fn read_only_binds_that_the_host_cannot_enforce_fail_verification in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:1061 nvx-rs/src/host/filesystem/tests.rs:1058
modified test fn explicit_targets_place_mappings_at_any_free_guest_path in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:1153 nvx-rs/src/host/filesystem/tests.rs:1151
modified test fn policy_paths_hide_show_and_narrow_writes in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:1358 nvx-rs/src/host/filesystem/tests.rs:1356
modified test fn narrowed_writes_pin_only_what_lies_in_writable_paths in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:1522 nvx-rs/src/host/filesystem/tests.rs:1522
modified test fn the_caller_owner_reaches_openvmm_on_linux_hosts in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:1600 nvx-rs/src/host/filesystem/tests.rs:1617
modified test fn read_write_mappings_to_which_writes_are_narrowed_stay_visible in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:1660 nvx-rs/src/host/filesystem/tests.rs:1679
modified test fn denied_mapped_directories_inside_denied_ones_show_their_allowed_paths in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:1722 nvx-rs/src/host/filesystem/tests.rs:1742
modified test fn recorded_plans_hold_no_more_policy_than_one_aggregate_accepts in host::filesystem::tests nvx-rs/src/host/filesystem/tests.rs:1773 nvx-rs/src/host/filesystem/tests.rs:1797
modified test fn shares_are_planned_through_the_export_planner in host::sandbox::mount::tests nvx-rs/src/host/sandbox/mount/tests.rs:512 nvx-rs/src/host/sandbox/mount/tests.rs:512
modified test fn launch_validation_rejects_overlapping_or_foreign_host_paths in host::sandbox::tests nvx-rs/src/host/sandbox/tests.rs:260 nvx-rs/src/host/sandbox/tests.rs:261
modified test fn launch_validation_rejects_shares_that_came_to_overlap in host::sandbox::tests nvx-rs/src/host/sandbox/tests.rs:414 nvx-rs/src/host/sandbox/tests.rs:415
modified test fn launch_validation_requires_plain_mount_directory in host::sandbox::tests nvx-rs/src/host/sandbox/tests.rs:491 nvx-rs/src/host/sandbox/tests.rs:493
modified test fn capture_creation_refuses_layered_roots_and_exports_without_creating_outputs in host::vm::tests nvx-rs/src/host/vm/tests.rs:435 nvx-rs/src/host/vm/tests.rs:435
modified test fn prepared_record_keeps_checked_root_view_export_resources_and_transaction in host::vm::tests nvx-rs/src/host/vm/tests.rs:621 nvx-rs/src/host/vm/tests.rs:619
modified test fn start_refuses_recorded_shares_of_its_own_files in cli/lifecycle (tests) nvx-rs/tests/cli/lifecycle.rs:1806 nvx-rs/tests/cli/lifecycle.rs:1847

Relocated unchanged: 0; added: 3. Compared the merge base 19b5404dd0d2 with 03b7ccb22dc7.

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

Cleanup rejects older restored sandbox records missing export_pins, blocking deprovisioning and leaving the openvmm view and state behind.

1 open issue
What changed in this PR

This implements SL6.5b of #1125, separating public export descriptions from CLI-owned file identity tracking as part of NVX’s library slimming.

Changes:

  • Makes ExportPlan path-only and introduces private PlannedExport and Pins types for planning and verification.
  • Persists pins separately as export_pins and checks them before sandbox launches.
  • Updates tests, API snapshots, and documentation for the new representation and caller responsibilities.
File Description
nvx-rs/​tests/​cli/​openvmm.rs Removes identity data from expected export fixtures.
nvx-rs/​tests/​cli/​lifecycle.rs Checks separate pin records and malformed configurations.
nvx-rs/​src/​host/​vm/​tests.rs Uses path-only export fixtures.
nvx-rs/​src/​host/​sandbox/​tests.rs Adapts planning and verification tests to PlannedExport.
nvx-rs/​src/​host/​sandbox/​options/​tests.rs Reads lowering inputs from the export description.
nvx-rs/​src/​host/​sandbox/​mount/​tests.rs Checks descriptions and pins separately.
nvx-rs/​src/​host/​sandbox/​mount/​aggregate.rs Returns planned exports with their pins.
nvx-rs/​src/​host/​filesystem/​verify.rs Moves pin checks to PlannedExport and internalizes lowering helpers.
nvx-rs/​src/​host/​filesystem/​tests.rs Adapts verification tests and adds pin-slot validation cases.
nvx-rs/​src/​host/​filesystem/​policy.rs Gates writable-boundary helpers behind cli.
nvx-rs/​src/​host/​filesystem/​mod.rs Separates descriptions from pins and documents path stability.
nvx-rs/​src/​host/​config/​tests.rs Removes identities from configuration fixtures.
nvx-rs/​src/​host/​cli/​sandbox/​state/​tests.rs Adds persistence and changed-pin rejection coverage.
nvx-rs/​src/​host/​cli/​sandbox/​state.rs Stores, reloads, and validates exports with separate pins.
nvx-rs/​src/​host/​cli/​sandbox/​mod.rs Carries planned exports through sandbox setup.
nvx-rs/​src/​host/​cli/​sandbox/​lifecycle/​start/​tests/​restore.rs Uses path-only exports in restore fixtures.
nvx-rs/​src/​host/​cli/​sandbox/​lifecycle/​start/​tests.rs Adapts command-line checks to wrapped exports.
nvx-rs/​src/​host/​cli/​sandbox/​lifecycle/​mod.rs Includes pins in provisioning inputs.
nvx-rs/​src/​host/​cli/​run/​guest_tokens/​tests.rs Tests guest tokens using the export description.
nvx-rs/​src/​host/​cli/​run.rs Passes only the description to one-shot VM configuration.
nvx-rs/​README.md Documents API boundaries, path stability, and stored pins.
nvx-rs/​public-api/​windows.txt Removes identity fields and internalized methods from the Windows API snapshot.
nvx-rs/​public-api/​linux.txt Records the same API removals for Linux.
doc/​run.md Explains separate pin storage and older-record rejection.
doc/​acceptance-case-matrix.md Links the new persistence test to pin-verification acceptance coverage.

Review effort: Balanced

Comment thread nvx-rs/src/host/cli/sandbox/state.rs Resolved
ExportPlan, the public description of a VM's export, combined the host
paths with the file identities that only the CLI's store needs: each
child's denied_identities, each bind's identity, and FileIdentity, with
check's rules that a recorded plan pins each object that a workload
could move. The paths are what nvx lowers; the identities are the CLI's
recheck of its own records before each start.

The CLI now owns PlannedExport { description: ExportPlan, pins: Pins },
crate-private and cli-gated beside the planner and the verifier that
SL6.5a2 made private:

- The planner returns a PlannedExport: the description, and the pins
  of each child's denied paths and of each bind, in their orders,
  which it computes as before. The binds keep their pins as they are
  sorted.
- PlannedExport::check runs ExportPlan::check, then the pin rules that
  moved out of it: a slot for each child and each of its denied paths,
  one for each bind, and the identity of each denied or mapped object
  strictly inside a part that the guest may write, with the same
  messages. PlannedExport::verify is the old ExportPlan::verify, which
  reads the identities from the pins.
- The sandbox's config.json keeps the description under export, paths
  only, where CI's share checks read it, and records the pins beside it
  under export_pins, as it records each image beside its binding.
  Config::new requires the field and refuses an export without pins or
  pins without an export as malformed; Config::check checks and
  verifies the pair before vm_config passes the description to the VM.
  plan_mounts, provision's ProvisionConfig and check_images carry the
  PlannedExport; nvx-rs run, which launches its plan at once and never
  again, keeps the description and checks no pins, as before.

nvx's public API loses FileIdentity, ExportChild::denied_identities,
Bind::identity, and ExportPlan::openvmm_arguments and kernel_token,
which only VmConfig's lowering calls and which become pub(crate);
lowering is unchanged. ExportPlan::check stays structural and needs no
identity data. host::filesystem documents what the caller keeps stable:
the launch race, the writable-part recheck and durable denials.

A configuration that an earlier build wrote has no export_pins, so a
start refuses it as "sandbox configuration has no export pins;
deprovision the sandbox and provision it again", as for every field
that the record gained before; nothing is migrated, and the format
stays 10.

Tests: the planner's and verifier's unit tests read the pins from the
PlannedExport, and narrowed_writes_pin_only_what_lies_in_writable_paths
also checks the new slot rules; ExportPlan's own tests and fixtures,
in host::config, host::vm, the restore tests and tests/cli/openvmm.rs,
build plans without identities. A new state test records an export
with its pins, reads both back, and refuses a moved, a gone and an
uninspectable pinned object at Config::check. The CLI's lifecycle
tests expect export_pins in each record, and malformed_configuration
refuses a record without it, an export without pins, and pins without
an export.

Part of #1125 (SL6.5b).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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 were identified for the separation of public export descriptions from CLI-managed identity pins.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) merged commit 14ea1a4 into feature/nvx-refactor Nov 12, 2026
87 checks passed
@ppenna
Pedro Henrique Penna (ppenna) deleted the agents/slim-sl6-5b-export-paths-only branch November 12, 2026 22:27
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.

2 participants