Repository navigation
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.
- Reference code, not API. The guidelines (nvx-rs slimming 5.2.1: the library's guide, its design and the guest-agent guidelines #1130) describe the space's interface for other inits; nvx-rs slimming 4.3.4: NVXC, the host's platform layer and the guest's space are private #1127 makes the code crate-private where it is.
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.
-
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
MappingTablestay 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 ofguest::mappingsand the activation'sapplyfamily (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::mappingssplits:git mvofguest/mappings.rstoguest/mappings/mapper.rs, which keeps the mapper (Mapper,TargetRoot,SourceRoot,Reserved,bind,Step,MappingError,ApplyError,UnmountFailureandTeardownError, on the kernel seam), and a newmappings.rs, whose lines count, takes back the module's docs and the mapping values (Mapping,Access,Kind,InvalidMapping,MAX_PATH_BYTES), which stay public withMappingTable,TableErrorandRefusal. 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.rstoguest/mappings/mapper.rs, and movemappings/tests.rsplustests/privileged.rsto the mapper's tests tree; they keep the fake kernel re-exported byguest::root::testsand their test-onlyMappedandTeardownErrormethods. - Create a small
guest/mappings.rsfacade with the module docs and publicMapping,Access,Kind,InvalidMapping,MAX_PATH_BYTES,MappingTable,TableErrorandRefusal. - Mark
guest::{root, gpt},mappings::mapperand activation'sapply,Applied,RootTemplate,ApplyError,AppliedTeardownErrorandNetworkErroras 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
guestmodule docs.
- Move the mapper from
- 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
MappingTableor 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.
- Behavior or source changes for current users:
- Approach: Use
git mvto 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.
- Objective: The guest's space remains usable at the API-ready point but stops presenting itself as supported public API. Public mapping values and
-
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)andRuntimeMounts, in the hiddenroot:nosuid,nodevon the tree first;/proc,/sys(read-only),/dev,/dev/pts,/dev/shmand/runin the root; the time status bound read-only at/run/nvx/timewhen asked; the mounts undone if one fails. From the mount code ofsys::entry. - Scope:
- Add
Root::mount_runtime(time_status)andRuntimeMountsto the hiddenguest::rootmodule. - Apply
nosuid,nodevto the root tree before mounting/proc, read-only/sys,/dev,/dev/pts,/dev/shmand/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::entryrather than adding another mount engine.
- Add
- 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::entryinto methods owned byRoot, 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_runtimeappliesnosuid,nodevbefore any child mount.- Tests observe
/proc, read-only/sys,/dev,/dev/pts,/dev/shmand/runat 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.
-
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::switchandSwitchError: move the root onto/, thenchrootandchdir. After the switch, teardown refuses, and dropping the root unmounts nothing. Tests show thatTargetRootandSourceRootopened before the switch still map after it. - Scope:
- Add
Root::switchandSwitchErrorto the hiddenguest::rootmodule. - Move the assembled root mount onto
/, then callchrootandchdirin that order. - Record a distinct switched state after a successful switch so teardown refuses and dropping the root performs no unmount.
- Keep
TargetRootandSourceRootdescriptors opened before the switch valid for mappings afterwards.
- Add
- 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,chrootandchdirsequence intoRoot, 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.
TargetRootandSourceRootopened before the switch still apply mappings afterwards.
- A root switches once by moving onto
- Needs: SL7.1a, above.
- Estimate: 200 core lines, 2 counted files. Past 400 core lines, stop and ask before continuing.
- Objective: An init can make an assembled root become
-
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
Rootowns 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 withsethostname, the machine ID, and the resolver file with the DNS server, with their binds, which the root records:Root::teardownundoes 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). Fromguest::entryand activation'sapply:guest::entry::{MachineId, DEFAULT_HOSTNAME, MAX_HOSTNAME_BYTES},MACHINE_ID_FILEandRESOLVER_FILEmove 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_FILEandRESOLVER_FILEfrom entry and activation application intoguest::view. - Set the hostname with
sethostname; keep the image's machine ID forMachineIdMode::Image, or generate and bind one forGenerated. - 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_BYTESwhere it is.
- Add hidden
- 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::applysets the VM hostname and returns the machine ID it selected.MachineIdMode::Imageleaves the image's/etc/machine-id, whileGeneratedbinds 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.
- Objective: The guest's hostname, machine ID and resolver file become one VM-wide view operation, and
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
guest: hide the guest's space as internal reference code
guest: refuse a second runtime mount before restricting anything
guest: hide the guest's space as internal reference code
guest: mount the runtime before any share, and map beneath its /run
guest: mount the runtime's file systems in a layered root
guest: mount the runtime's file systems in a layered root
guest: mount the runtime's file systems in a layered root
guest: switch into a layered root once
guest: mount the runtime's file systems in a layered root
guest: switch into a layered root once
guest: switch into a layered root once
esaurez commentedon Nov 10, 2026
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.
SL7.0 - merged via pull request 1262.
esaurez/slim-2.4.1-sl7.0-20261103at6c6b40f2e7d5c7820e43a6106f3ea7dec2e5bb6e.devat586ccf8826f9ad8ce6d753f630bcdb0c585a45e8; merge commit6bcb68fce00e2b9bf29d35cc4bc9f2a2b57c1f19.SL7.2 - merged via pull request 1339.
esaurez/slim-2.4.1-sl7.2-20261106at207548c5cf04626eaaf021a42684e562f58f8491.feature/nvx-refactorat708dcef59a862160d6ae54b6ff0ebb3d7bd4d469; merge commitea2e02412b3ca14492bbfec44487ede486249bce.SL7.1a - merged via pull request 1276.
esaurez/slim-2.4.1-sl7.1a-20261103ate4d4bca74ec944cf03eca8a7cad11978c8a0edf2.feature/nvx-refactorat53ad09678bc146880097ef8f3937f97a2719b014; merge commitd8f1e45d14dce3b525f958f52d7895b8ba2ac840.SL7.1b - merged via pull request 1295.
esaurez/slim-2.4.1-sl7.1b-20261103at203891ffe84626a584947e71a787c29119417936.feature/nvx-refactorat3d243e59873998f6f6b28cfa403e81c5d224a51a; merge commit4114e87dc907873b95b3d092fb356df5fbcaf230.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.
guest: mount the runtime's file systems in a layered root
guest: switch into a layered root once
guest: mount the runtime's file systems in a layered root
guest: switch into a layered root once