Skip to content

nvx-rs slimming 2.4.1: the guest's space becomes reference code, with runtime mounts, one switch and the view #1104

Closed

Description

Parent: #1082, in #1076. Needs: #1091, #1098. Unblocks: #1105, #1115, #1118.

Goal. The guest's space (guest::{root, gpt}, the mapper of guest::mappings and the activation's apply family) becomes #[doc(hidden)], documented as unstable and becoming crate-private once nothing outside NVX uses it, with no change for its users; the mapping values and MappingTable stay public. It then gains what an init does once per VM: Root::mount_runtime, Root::switch, and view::apply, which sets the hostname, the machine ID and the resolver file for the whole VM, and records their binds in the root.

Notes.

PRs, in order. Each PR has one goal, leaves dev green, and names in its description each old test of its area as kept, rewritten or retired, with each change that check_test_changes.py reports. Its tests follow the repository's test layout: a module's unit tests, and the code that only they use, sit in its tests.rs or tests/ and move with it. The estimates count core lines (code, not comments, docs or tests) and counted files; each PR records its measured values. Each PR does what its Scope says and no more: a case, input or layout that its Scope doesn't name, and whatever "Not in this PR" lists, is out of scope. Address every review comment within the scope, and ask before acting on one that asks for more. A PR whose measured core lines pass the stop that its Estimate names (twice the estimate, at most 600) stops and asks before continuing.

  1. SL7.0: The guest's space is NVX's internal reference code, not public API.

    • Objective: The guest's space remains usable at the API-ready point but stops presenting itself as supported public API. Public mapping values and MappingTable stay visible; the mapper, root, GPT and activation application become hidden, unstable internal reference code that becomes crate-private once nothing outside NVX uses it.
    • Contents: guest::{root, gpt}, the mapper of guest::mappings and the activation's apply family (apply, Applied, RootTemplate, ApplyError, AppliedTeardownError, NetworkError) become #[doc(hidden)], documented as unstable internal reference code that becomes crate-private once nothing outside NVX uses it (SL6.13b). First, guest::mappings splits: git mv of guest/mappings.rs to guest/mappings/mapper.rs, which keeps the mapper (Mapper, TargetRoot, SourceRoot, Reserved, bind, Step, MappingError, ApplyError, UnmountFailure and TeardownError, on the kernel seam), and a new mappings.rs, whose lines count, takes back the module's docs and the mapping values (Mapping, Access, Kind, InvalidMapping, MAX_PATH_BYTES), which stay public with MappingTable, TableError and Refusal. The guest module's docs list the public surface, and say that the hidden space is unstable internal reference code that becomes crate-private once nothing outside NVX uses it. No user changes: nvx-init, the test adapter and downstream users pinned to the API-ready commit still use them.
    • Scope:
      • Move the mapper from nvx-rs/src/guest/mappings.rs to guest/mappings/mapper.rs, and move mappings/tests.rs plus tests/privileged.rs to the mapper's tests tree; they keep the fake kernel re-exported by guest::root::tests and their test-only Mapped and TeardownError methods.
      • Create a small guest/mappings.rs facade with the module docs and public Mapping, Access, Kind, InvalidMapping, MAX_PATH_BYTES, MappingTable, TableError and Refusal.
      • Mark guest::{root, gpt}, mappings::mapper and activation's apply, Applied, RootTemplate, ApplyError, AppliedTeardownError and NetworkError as hidden from generated docs.
      • Document the hidden space as unstable internal reference code that later becomes crate-private, and list the supported public guest surface in guest module docs.
    • Not in this PR:
      • Behavior or source changes for current users: nvx-init, the test adapter and users pinned to this point still compile against the hidden items.
      • Making the space crate-private: SL6.13b does that once nothing outside NVX uses it.
      • Hiding MappingTable or the mapping values, or changing their API: they remain public guest contracts.
      • Renames, mapper redesign, cleanup or new visibility abstractions beyond the split required to hide the mapper.
    • Approach: Use git mv to put the existing mapper behind a hidden submodule. Move the mapping values and table exports into the thin public facade, and re-export the mapper's existing public items there under #[doc(hidden)] so their paths keep working. Do not change implementations or add wrappers.
    • Done when:
      • Current users and tests of the space still compile and behave unchanged.
      • Generated documentation shows the public guest surface and omits the root, GPT, mapper and activation application family.
      • Mapping values and MappingTable, including their existing methods and errors, remain publicly documented.
      • The hidden modules state that they are unstable internal reference code that later becomes crate-private.
    • Needs: SL0.4, in nvx-rs slimming 1.1.1: the public API is checked, and NVX's programs are hidden from it #1091.
    • Estimate: 350 core lines, 4 counted files. Past 600 core lines, stop and ask before continuing.
  2. SL7.1a: The space mounts its root's runtime filesystems.

    • Objective: The space gains the one-time runtime filesystem setup needed before a VM switches into its root. It applies the root-wide safety flags first, mounts the fixed Linux runtime filesystems, optionally binds the time status read-only, and rolls back a partial setup.
    • Contents: Root::mount_runtime(time_status) and RuntimeMounts, in the hidden root: nosuid,nodev on the tree first; /proc, /sys (read-only), /dev, /dev/pts, /dev/shm and /run in the root; the time status bound read-only at /run/nvx/time when asked; the mounts undone if one fails. From the mount code of sys::entry.
    • Scope:
      • Add Root::mount_runtime(time_status) and RuntimeMounts to the hidden guest::root module.
      • Apply nosuid,nodev to the root tree before mounting /proc, read-only /sys, /dev, /dev/pts, /dev/shm and /run.
      • When a time-status path is supplied, bind it read-only at /run/nvx/time.
      • Undo every runtime mount made by the call if a later mount fails.
      • Reuse the existing mount operations from guest::sys::entry rather than adding another mount engine.
    • Not in this PR:
      • Additional filesystems, configurable mount tables or platform support beyond the fixed Linux guest layout.
      • Changing per-execution mounting elsewhere or deleting its callers; later init work switches consumers to this root-wide setup.
      • Creating directories or policies unrelated to the named runtime mounts.
      • Switching the root or applying the VM view; SL7.1b and SL7.2 do those.
    • Approach: Extract the existing mount calls and flags from guest::sys::entry into methods owned by Root, preserving their current system seam for tests. Use a fixed ordered list and the root's existing teardown bookkeeping; do not add a generic mount planner or configuration format.
    • Done when:
      • mount_runtime applies nosuid,nodev before any child mount.
      • Tests observe /proc, read-only /sys, /dev, /dev/pts, /dev/shm and /run at the root's fixed paths.
      • Supplying a time-status file creates a read-only bind at /run/nvx/time, while omitting it creates none.
      • A failure at any mount removes the mounts already made by that call.
    • Needs: SL7.0, above.
    • Estimate: 250 core lines, 3 counted files. Past 500 core lines, stop and ask before continuing.
  3. SL7.1b: An init switches into its root once.

    • Objective: An init can make an assembled root become / once for the whole VM. After that irreversible switch, the space refuses teardown and its drop path leaves the root mounted, while descriptors opened beforehand remain usable.
    • Contents: Root::switch and SwitchError: move the root onto /, then chroot and chdir. After the switch, teardown refuses, and dropping the root unmounts nothing. Tests show that TargetRoot and SourceRoot opened before the switch still map after it.
    • Scope:
      • Add Root::switch and SwitchError to the hidden guest::root module.
      • Move the assembled root mount onto /, then call chroot and chdir in that order.
      • Record a distinct switched state after a successful switch so teardown refuses and dropping the root performs no unmount.
      • Keep TargetRoot and SourceRoot descriptors opened before the switch valid for mappings afterwards.
    • Not in this PR:
      • A reversible switch, switching more than once or recovering the old root: the VM ends by syncing and powering off.
      • Opening required descriptors or starting helpers automatically; callers must do that before the switch.
      • Per-execution root switching or namespace redesign.
      • Runtime mounts or view application; SL7.1a and SL7.2 own those steps.
    • Approach: Move the existing MS_MOVE, chroot and chdir sequence into Root, then record a switched state distinct from the existing failure latch. Reuse open directory descriptors across the switch; do not add an old-root handle, rollback mechanism or switch abstraction.
    • Done when:
      • A root switches once by moving onto /, then changing root and working directory.
      • After a successful switch, explicit teardown refuses and dropping the root issues no unmount.
      • A second switch is refused.
      • TargetRoot and SourceRoot opened before the switch still apply mappings afterwards.
    • Needs: SL7.1a, above.
    • Estimate: 200 core lines, 2 counted files. Past 400 core lines, stop and ask before continuing.
  4. SL7.2: The view is applied once, for the whole VM, and the root owns its binds.

    • Objective: The guest's hostname, machine ID and resolver file become one VM-wide view operation, and Root owns every bind it creates. A teardown before the root switch removes those binds; after the switch nothing unmounts them.
    • Contents: guest::view, hidden like the rest of the space: apply(&mut Root, &View, dns) -> Result<MachineId, ViewError>: the hostname with sethostname, the machine ID, and the resolver file with the DNS server, with their binds, which the root records: Root::teardown undoes them before the switch, and nothing after it (nvx-rs: give a layered root's workloads the DNS server that their network policy allows #860). From guest::entry and activation's apply: guest::entry::{MachineId, DEFAULT_HOSTNAME, MAX_HOSTNAME_BYTES}, MACHINE_ID_FILE and RESOLVER_FILE move here; the record's own limit, activation::MAX_HOSTNAME_BYTES, stays.
    • Scope:
      • Add hidden guest::view::apply(&mut Root, &View, dns) -> Result<MachineId, ViewError>.
      • Move MachineId, DEFAULT_HOSTNAME, the guest-entry hostname limit, MACHINE_ID_FILE and RESOLVER_FILE from entry and activation application into guest::view.
      • Set the hostname with sethostname; keep the image's machine ID for MachineIdMode::Image, or generate and bind one for Generated.
      • When DNS is supplied, write and bind the resolver file; record the machine-ID and resolver binds in Root.
      • Keep activation's record-specific MAX_HOSTNAME_BYTES where it is.
    • Not in this PR:
      • Per-execution hostname or resolver setup: this operation applies the view once for the VM.
      • DNS search domains, multiple servers or resolver configuration beyond the one DNS server carried by the record.
      • Making the whole space crate-private: SL6.13b does that.
      • Changing the activation record's hostname limit or machine-ID modes.
    • Approach: Move the existing hostname, machine-ID and resolver operations into one hidden module and have them register binds with Root's existing teardown ownership. Reuse current file formats and system calls; do not introduce a view object, resolver builder or generalized file-bind registry.
    • Done when:
      • view::apply sets the VM hostname and returns the machine ID it selected.
      • MachineIdMode::Image leaves the image's /etc/machine-id, while Generated binds a generated value over it.
      • Supplying DNS writes and binds the resolver file; omitting DNS creates no resolver bind.
      • Teardown before the switch removes the view's binds, and the switched root leaves them mounted.
    • Needs: SL7.0, above; SL2.4a2, in nvx-rs slimming 2.1.2: the kernel command line and OpenVMM's arguments come from the configuration alone #1098.
    • Estimate: 280 core lines, 4 counted files. Past 560 core lines, stop and ask before continuing.

Tests. Hiding the space breaks none of its current users, and the rustdoc shows only the public guest surface. mount_runtime applies nosuid,nodev first; mounts /proc, /sys read-only, /dev, /dev/pts, /dev/shm and /run; binds the time status read-only at /run/nvx/time when asked; and undoes its mounts if one fails. switch runs once; afterwards teardown refuses and dropping the root unmounts nothing, and TargetRoot and SourceRoot opened before it still map. view::apply sets the VM's hostname; leaves the image's own /etc/machine-id with MachineIdMode::Image, or generates one and binds it over the image's with Generated; writes and binds the resolver file when given a DNS server; and a teardown before the switch undoes its binds (#860).

Done when: every PR above has merged, with dev green.

Activity

  1. esaurez commented on Nov 10, 2026

    @esaurez
    ContributorAuthor

    Workstream source/progress checkpoint - 2026-11-10 UTC

    Checked boxes below mean merged scope, not merely an implemented first iteration. Implementation branches are source checkpoints, not automatic qualification, Ready or merge approval. Some starting inputs are frozen/anonymous commits rather than standalone branches; their exact SHAs and known merged producer refs are recorded explicitly.

    The original technical scope remains authoritative, supplemented only by any portable technical clarification in this issue. No machine-specific execution policy is part of this checkpoint.

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

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions