Skip to content

guest: map host paths beneath root descriptors - #774

Merged
Enrique Saurez (esaurez) merged 5 commits into
devfrom
esaurez/a3-2-rooted-mappings-20261021
Oct 23, 2026
Merged

Enrique Saurez (esaurez) merged 5 commits into
devfrom
esaurez/a3-2-rooted-mappings-20261021

Conversation

@esaurez

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

Copy link
Copy Markdown
Contributor

Part of #621: A3.2, descriptor-rooted mappings, also used by the mapping table and the runtime binds.
Refs #619, #622, #535.

What and why

Host mappings were bind-mounted by path name. G4's Mapping created a missing target by path, bind-mounted the source, and only then remounted it nosuid, nodev, and read-only, so a symbolic link, a magic link, or a rename that raced the walk could redirect a bind, and a read-only bind was briefly writable. This rebuilds nvx::guest::mappings as a mapper that works beneath descriptors, and moves both of its callers onto it. No pathname path is left.

The mapper, nvx::guest::mappings:

  • A TargetRoot is the descriptor of the root of a mount, such as a layered root's overlay or a guest's /, with the unique mount IDs of the root's own mounts. Each of those mounts must show its whole file system, which statmount tells: on a bind of a directory below its file system's root, a rename through another mount of that file system could move a directory that the walk opened out of the root. A SourceRoot is the descriptor of the sources' root, such as the guest's mount of the host's export.
  • Walk: each component of a target is opened with openat2 beneath the one before it, from the root's descriptor, with RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS | RESOLVE_NO_MAGICLINKS. A source is opened in one call, with RESOLVE_NO_XDEV too.
  • Creation: a missing component is created only where statx shows its parent on one of the root's own mounts, by STATX_MNT_ID_UNIQUE: never inside another mapping, nor on another mount, a bind of the root's own file system included, which has the same st_dev. It gets exactly mode 0755, or 0644 for a file target, whatever the caller's umask or a default ACL of its parent, as a layered root's live shares did, so that a workload that is not root can enter it. A file is created with O_CREAT | O_EXCL and gets its mode through the descriptor that the creation returns, which refers to that file alone. A directory is created with no permissions, which neither the umask nor an ACL can narrow; the mapper then opens it as an existing component and, through that descriptor, gives it 0755 only if it still has no permissions and the mapper's user as its owner, as the one that it created has. No other user can give a directory both, nor move one that has both out of another directory, which takes the permission to write it: of what a rename could put in its place, only such a directory from the same parent would get the mode. One that existed already keeps its mode.
  • Checks: kinds, overlaps between entries, and reserved targets and their ancestors: the /run/nvx/time tree always, plus the root profile's Reserved set. Reserved::container_root() takes NVX's container-root set from nvx::protocol::share, and an adapter passes its own. A target that is the root of a mount already is refused, unless the root owns that mount, such as the tmpfs at a guest's /tmp, which the mapping then covers until the teardown.
  • Attach: open_tree(OPEN_TREE_CLONE) copies the source's mount, detached; mount_setattr sets nosuid, nodev, and the read-only flag on the copy; only then does move_mount attach it onto the target's descriptor. The mapper then finds the target again from the root, and detaches a mapping that a rename moved meanwhile. If that detach fails, the mapper keeps the mount's descriptor, and its teardown detaches it first, or reports it.
  • Order and failure: zero entries do no work, entries apply in order, and the first failure latches the mapper. It tracks its mappings, and its teardown unmounts them in reverse order through each parent's descriptor, and reports each failure.
  • Runtime binds: bind applies one bind, such as the machine ID or the time status, through the same walk and attach, for the execution entry that follows (A3.4).

Syscalls: guest/sys/rooted.rs adds safe wrappers for openat2, statx with the owner, the mode, and the unique mount ID, statmount for a mount's root, mkdirat, an exclusive creation of a file with its exact mode, open_tree, mount_setattr, and move_mount, and three calls that name a descriptor through the calling thread's /proc/thread-self/fd, so that a thread with a descriptor table of its own reaches what its descriptors refer to: a change of an O_PATH descriptor's mode, which fchmod does not take, and the unmounts beneath a parent's descriptor and of a detached tree. The constants that libc lacks come from the kernel's UAPI headers.

Callers:

  • A layered Root mounts a live share at staging in its runtime directory, then moves it into the root through its mapper; it binds an aggregate's children through the mapper; and Root::map applies host mappings beneath the overlay. A failure after a request's checks latches the root, and the teardown unmounts the mapper's mappings first. The root's own pathname checks and creation are gone.
  • NVX's managed agent applies its direct-mode MAPS table through a mapper beneath /, which creates targets only on / and on the tmpfs at /tmp, and latches at the first failure, as before. A mapping of the host's /tmp itself, which the host's planner accepts, covers that tmpfs, as the pathname mapper's bind did.

Tests:

  • The root's fake kernel, from nvx-rs: add guest operations for a layered root #674, simulates mount IDs, detached trees, and the new calls. Unit and fault-injection tests cover target roots of whole file systems, the walk's refusals, creation only on the root's own mounts, with exact modes under a umask of 077 and below a restrictive default ACL, for the mapper and a layered root's live shares, and under the umask for the managed agent's table, a failure to give a created directory its mode, a mapping that covers the root of one of them, and the managed agent's mapping of /tmp itself, a directory that a rename puts in place of a created one before it gets its mode, overlaps and reserved targets, the attach order with the flags before move_mount, latching, zero entries, a moved mapping that the attach fails to detach, and the teardown's order and failures.
  • Privileged tests (#[ignore], run as root) cover, on a real kernel, a symbolic link and a magic link at every component, a rename race during the walk, kinds, a target nested in another mapping, reserved targets and ancestors, a mapping that covers the root of a mount that the root owns until the teardown, and is refused on one that it does not, a workload that is not root reading through the directories that a mapper created under a umask of 077 and below a restrictive default ACL, a read-only mapping that is never visible writable, the teardown, also from a thread with a descriptor table of its own, and a bind of a subdirectory refused as a root or as one of its mounts.
  • The runtime adapter's worker gains probes that map beneath its assembled layered root, through Root::map and through mappers with reserved sets of its own. Two VM scenarios check them: mappings_apply_beneath_the_root and a_refused_mapping_latches_the_root.

One PR rather than two: the task allows splitting the mapper from the managed agent's switch if they grow large. Mapping is rebuilt in place, so both of its callers move with it, or the pathname code would have stayed for a PR. The managed agent's part is about 150 lines of code and 250 of tests. The two commits keep the library apart from the adapter's scenarios.

Direct mode's reserved targets: the managed agent's direct mode reserves the runtime's tree, as every mapper does, and nothing more, as before; the host's planner refuses its own reserved targets before it sends MAPS. The sandbox mode's MAPS, beneath the container root with its reserved set, come with A4.

Validation

On Linux (kernel 6.18, as a non-root user), the checks of check-nvx-rs and check-aci-edge-sandboxes:

  • in nvx-rs: cargo fmt --all --check; cargo clippy --locked --all-targets --all-features -- -D warnings; cargo clippy --locked --lib --no-default-features -- -D warnings; cargo test --locked --all-features; cargo test --locked; cargo doc --locked --no-deps --all-features with RUSTDOCFLAGS=-D warnings; cargo +1.89 check --locked --all-targets --all-features; the macOS check; the artifact bundling check;
  • python3 scripts/nvx.py check-guest-binaries, build-guest-binaries, and test-guest-binaries;
  • tests/runtime-adapter in its protocol-only, host-only, and guest-only configurations. The guest-only configuration's MSRV check ran for the x86_64-unknown-linux-gnu target; CI runs it for musl;
  • aci_edge_sandboxes: fmt, Clippy, tests, docs, MSRV, the macOS check, and python3 scripts/nvx.py test-agent-bundling.

On Windows: the nvx-rs checks above but the macOS check, the runtime adapter's protocol-only and host-only configurations, and aci_edge_sandboxes' Clippy, tests, MSRV check, and test-agent-bundling. python scripts/check_markdown_links.py passes.

The privileged tests, cargo test --all-features -- --ignored guest::mappings::privileged, pass inside real VMs on NVX's guest kernel, 6.18.38, on WHP and MSHV: a static musl build of the test binary ran as PID 1, with two processors. They also pass as root on a Linux host, with kernel 6.18, and passed in a KVM VM on an earlier base, before the last three commits. Each rename race had all three outcomes: attached, refused at a link, and moved and detached.

A sandbox that maps the host's /tmp read-only, run once by hand on MSHV in a private mount namespace whose /tmp was a fresh tmpfs, read the host's file at /tmp and showed the mapping above the guest's tmpfs, as with this PR's base. Without the commit that lets a mapping cover the root of a mount that the root owns, its start failed with EBUSY.

Real VMs, with an Alpine initramfs built from this branch:

Backend Where test-aci-edge-sandboxes test-microvm scenarios Runtime adapter vm_layered
WHP Windows 11 18 of 18 pass 6 of 6
MSHV a Linux VM, as root, --cpu-profile host 13 of 18 (see below) not run 6 of 6
KVM WSL, --cpu-profile host, on an earlier base, before the last three commits 18 of 18 pass 6 of 6
  • test-microvm ran filesystem-shares, filesystem-policy, filesystem-owner, filesystem-comma-paths, denied-filesystem-paths, filesystem-snapshot, managed-lifecycle, lifecycle, workload-identity, and structured-outcome, with --processors 1 2.
  • On MSHV, /dev/mshv is root-only there, so the tests ran as root, and five read-write mapping tests fail: host_paths_are_mapped_into_the_guest, host_paths_with_commas_keep_their_access, mapped_files_hide_their_siblings_and_names_may_hold_spaces, single_files_are_granted_without_their_directories, and unrelated_directories_keep_their_own_access. They fail identically, at the same assertions, with the initramfs built from this PR's base: the workload cannot write the root-owned host directories.

Sizes of the stripped guest binaries and the Alpine initramfs, before and after, both built from source:

Artifact Before After
nvx-managed-agent 487,968 496,160 (+8,192)
other guest binaries unchanged unchanged
initramfs.cpio.gz (Alpine) 8,216,979 8,222,812 (+5,833)

Left to CI: KVM on this base, the full microVM matrix, MSHV runs as a non-root user, and the benchmarks.

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.

🔵 Needs a closer look

The security-sensitive syscall, containment, and mount-race cleanup changes warrant human review alongside fresh privileged-kernel and cross-backend CI results.

No open issues 🎉

What changed in this PR

Advances #621’s descriptor-rooted mapping work by replacing pathname-based attachment in the guest library’s callers. Layered roots and the managed agent use the shared mapper to enforce containment and apply mount restrictions before attachment.

Changes:

  • Adds descriptor-relative syscall wrappers and integrates mapping ownership, failure latching, and teardown into layered roots and direct-mode MAPS.
  • Expands fake-kernel, privileged-kernel, and runtime-adapter coverage for containment, mount flags, and cleanup.
  • Documents the mapping APIs, guarantees, and VM scenarios.
File Description
tests/​runtime-adapter/​tests/​vm_layered.rs Adds mapping and failure-latching VM scenarios.
tests/​runtime-adapter/​src/​worker/​probes.rs Shares mount-table helpers with mapping probes.
tests/​runtime-adapter/​src/​worker/​mapping.rs Adds mapping enforcement and cleanup probes.
tests/​runtime-adapter/​src/​worker.rs Routes mapping probes to the mutable root.
tests/​runtime-adapter/​README.md Documents mapping probes and scenarios.
tests/​runtime-adapter/​Cargo.toml Enables mount operations for guest probes.
nvx-rs/​src/​lib.rs Updates the guest-library overview.
nvx-rs/​src/​guest/​sys/​rooted.rs Adds descriptor-relative filesystem and mount wrappers.
nvx-rs/​src/​guest/​sys/​mod.rs Exposes the new internal syscall wrappers.
nvx-rs/​src/​guest/​sys/​fd.rs Clarifies raw-descriptor usage in documentation.
nvx-rs/​src/​guest/​root/​tests.rs Tests mapper integration, latching, and teardown.
nvx-rs/​src/​guest/​root/​share.rs Routes live shares and host mappings through the mapper.
nvx-rs/​src/​guest/​root/​kernel.rs Extends the fake kernel with descriptor and mount operations.
nvx-rs/​src/​guest/​root.rs Adds mapper ownership and ordered cleanup.
nvx-rs/​src/​guest/​mod.rs Updates guest API documentation.
nvx-rs/​src/​guest/​mappings/​privileged.rs Adds real-kernel containment and mount-lifecycle tests.
nvx-rs/​src/​guest/​mappings/​kernel.rs Defines the mapper’s replaceable kernel interface.
nvx-rs/​src/​guest/​managed_agent/​seam.rs Supplies real and fake mapping kernels.
nvx-rs/​src/​guest/​managed_agent/​mod.rs Documents the expanded system seam.
nvx-rs/​src/​guest/​managed_agent/​mappings.rs Applies ordered MAPS through a persistent mapper.
nvx-rs/​src/​guest/​managed_agent/​loopback.rs Updates integration assertions for descriptor-based attachment.
nvx-rs/​src/​guest/​managed_agent/​dispatch.rs Updates mapping dispatch tests and fixtures.
nvx-rs/​README.md Documents descriptor-rooted APIs and lifecycle behavior.
doc/​project-structure.md Updates the guest-library description.
doc/​design/​4.1-guest-filesystem-and-image-model.md Describes containment and pre-attachment restrictions.
doc/​ci.md Documents adapter mapping coverage in VM jobs.

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.

🟡 Changes recommended

Host planning still accepts direct-mode /tmp mappings that the new guest mapper rejects, preventing workload startup.

1 open issue

Review effort: Balanced

Comment thread nvx-rs/src/guest/managed_agent/mappings.rs

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.

🔵 Needs a closer look

The security-sensitive syscall wrappers, rename-race containment, and mount teardown semantics warrant final human review.

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.

🔵 Needs a closer look

Security-sensitive mount attachment and cleanup require final human review, including resolution of the descriptor-table issue.

1 open issue

Review effort: Balanced

Comment thread nvx-rs/src/guest/sys/rooted.rs Outdated

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.

🔵 Needs a closer look

The security-sensitive containment and rollback rewrite warrants human review, with an unresolved live-share permissions regression also needing attention.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Preserve traversable permissions for newly created live-share parents

nvx-rs/​src/​guest/​root/​share.rs:415

This switch removes the fixed permissions previously applied to newly created live-share parents. With a caller umask of 077, mounting at /srv/new/share now creates the root-owned /srv/new with mode 0700, rather than 0755. The mount covers only share, so a non-root workload cannot traverse new, even though mount_share or mount_aggregate succeeds. Preserve traversable permissions for newly created live-share parents using a race-safe creation mechanism, without changing existing or replacement directories. Add a restrictive-umask regression test that checks access by the workload identity.

1 resolved since last review

Review effort: Balanced

@esaurez

Enrique Saurez (esaurez) commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Copilot's previously missed issue on f7e1157, Preserve traversable permissions for newly created live-share parents (nvx-rs/src/guest/root/share.rs:415), is fixed in 3711ac7, together with the default-ACL case of the review of 4351bdb, which that commit replaces.

The mapper, which creates what a live share's target lacks, now gives each missing directory exactly 0755, and a file target exactly 0644, whatever the caller's umask or a default ACL of the parent, as a layered root did before this PR, without changing anything that existed already or that a rename put in place of what it created. The thread on rooted.rs describes how.

Regression tests under a umask of 077 and below a default ACL of u::rwx,g::---,o::---:

  • guest::root: a live share at /srv/new/share and a file child at /opt/app/config leave /srv, /srv/new, /opt, and /opt/app with 0755, and the file with 0644.
  • guest::mappings: the mapper's own targets get the same modes, and a directory that existed with 0700 keeps it; the managed agent's table does under the umask too.
  • Privileged, a_workload_that_is_not_root_enters_what_a_mapper_created_whatever_the_umask_or_a_default_acl: a thread whose file system identity is nobody reads through the shares and files that a mapper created under the umask and below the ACL.

The description's validation reran on this head, and its sizes are updated: the managed agent is 8,192 bytes larger than on dev, as before the fix.

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

Inherited default ACLs can leave newly created mapping directories inaccessible to non-root workloads despite the fixed-mode guarantee.

1 open issue

Review effort: Balanced

Comment thread nvx-rs/src/guest/sys/rooted.rs Outdated

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.

🔵 Needs a closer look

Security-sensitive containment, rename-race handling, and mount teardown semantics warrant final human review despite no confirmed blocking findings.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

Base automatically changed from esaurez/adapter-layered-vms-20261021 to dev October 23, 2026 03:00
Rebuild nvx::guest::mappings as a mapper that works beneath descriptors
instead of path strings, so that no symbolic link, magic link or rename
can redirect a bind, and every bind has its flags before it is visible.

- The mapper walks each target component with openat2 and
  RESOLVE_BENEATH, RESOLVE_NO_SYMLINKS and RESOLVE_NO_MAGICLINKS from
  the root's descriptor, and opens each source the same way without
  crossing a mount. The root, and each mount added as its own, must
  show its whole file system, as statmount tells, so that no rename
  through another mount of a bound directory's file system can move
  what the walk opened out of the root.
- It creates a missing directory or file only on the root's own mounts,
  checked by unique mount ID, never inside another mapping or mount,
  with mode 0755 or 0644 before the umask, and then opens it as an
  existing component, so it changes nothing that a rename puts there.
- It checks kinds, overlaps between entries, and reserved targets with
  their ancestors: the runtime's /run/nvx/time tree always, plus the
  root profile's set, which the container root takes from
  protocol::share.
- It attaches each mapping as a detached copy (open_tree with
  OPEN_TREE_CLONE) whose flags mount_setattr sets before move_mount
  puts it on the target's descriptor, then checks that the mapping is
  at its target and detaches it if a rename moved it. If that detach
  fails, the mapper keeps the mount's descriptor and detaches it first
  at the teardown, or reports it.
- Entries apply in order, zero entries do no work, and the first
  failure latches the mapper. It tracks its mappings and unmounts them
  in reverse at the teardown, through each parent's descriptor, and
  reports any that stay.
- bind applies a single mapping, such as a runtime bind, through the
  same code.

guest/sys gains the wrappers: openat2, statx with the unique mount ID,
statmount for a mount's root, mkdirat, open_tree, mount_setattr,
move_mount, and the unmounts beneath a parent descriptor and of a
detached tree.

The root's live shares and aggregate children now attach through the
mapper beneath the overlay: a share mounts at a staging point in the
runtime directory and moves from there. Root::map applies host mappings
beneath the overlay, the root latches at any failure after a request's
validation, and its teardown unmounts the mapper's mappings first. The
managed agent's direct mode applies its MAPS table through a mapper
beneath /, which creates targets only on / and /tmp. The pathname
mapper and the root's own pathname checks are gone.

The fake kernel of the root's seam simulates mount IDs, detached trees,
binds of subdirectories and the new calls for the unit and
fault-injection tests. Privileged tests check the walk, a rename race,
kinds, nesting, reserved targets, read-only binds and roots on binds of
subdirectories on a real kernel.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c8311d40-fd44-4294-b42e-3c6cd185c00b
Enrique Saurez and others added 4 commits October 22, 2026 20:03
The runtime adapter's worker maps beneath the layered root that it
assembled, as an adapter maps the host paths of its sandboxes, through
the probes of a new worker::mapping module, and two layered scenarios
check them on a real hypervisor:

- mappings_apply_beneath_the_root maps through Root::map: zero mappings
  that do no work, then a table in order, each mapping with its access,
  nosuid and nodev, on targets that the mapper creates on the overlay.
  Through mappers of the adapter's own, it checks the refusal of a link
  and of a magic link at each component, of the wrong kinds, of a
  target that would be created inside another mapping, and of the
  runtime's, the container root's and the adapter's own reserved
  targets, and a read-only mapping that is never writable while another
  thread tries to open it for writing. The stop then unmounts the
  root's mappings before the overlay.
- a_refused_mapping_latches_the_root maps through a symbolic link that
  it plants in the root, which the root refuses, after which it refuses
  every mapping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c8311d40-fd44-4294-b42e-3c6cd185c00b
The mapper refused every target that was the root of a mount. In the
managed agent's direct mode, that refused a mapping of the host's /tmp,
whose guest target is the tmpfs that the guest's init mounts at /tmp and
that the table's mapper owns: the host's planner accepts that mapping,
and the pathname mapper bound it.

A target that is the root of one of the root's own mounts is now one
that the mapping covers until the teardown. The root of any other mount,
an earlier mapping's included, is still refused.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c8311d40-fd44-4294-b42e-3c6cd185c00b
unmount_beneath and detach_mount named their descriptors through
/proc/self/fd, which shows the descriptor table of the process's first
thread. A thread with a table of its own, as after unshare(CLONE_FILES),
would then name another file than its descriptor's, or none, and its
teardown would fail or unmount something else. They now use
/proc/thread-self/fd, as the fake kernel and the tests' read of the
umask do too.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c8311d40-fd44-4294-b42e-3c6cd185c00b
A layered root created the missing parents and targets of its live
shares with mode 0755, or 0644 for a file, whatever the umask or a
default ACL of their parents, which a change of mode by path overrode.
The mapper that creates them now left their modes to the caller's umask
and to any default ACL instead: under a umask of 077, or below a
directory whose default ACL is u::rwx,g::---,o::---, a share at
/srv/new/share left /srv/new with mode 0700, which a workload that is
not root cannot enter.

The mapper now creates each missing file exclusively and sets its mode
through the descriptor that the creation returns, which refers to that
file alone. It creates each missing directory with no permissions,
which neither the umask nor an ACL can narrow, and, once the walk opens
it, gives it its mode through that descriptor, but only if it still has
no permissions and the mapper's user as its owner, as the directory
that it created has. A directory that a rename put in its place keeps
its mode, as does everything that existed already. A directory keeps
the set-group-ID bit that it inherits from its parent, as with mkdir.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c8311d40-fd44-4294-b42e-3c6cd185c00b
Copilot AI balanced review requested due to automatic review settings October 23, 2026 03:13
@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/a3-2-rooted-mappings-20261021 branch from 3711ac7 to a48857c Compare October 23, 2026 03:13

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.

🔵 Needs a closer look

Security-sensitive descriptor containment, rename-race handling, and mount teardown warrant final human review alongside the reported privileged-kernel validation.

No open issues 🎉

Review effort: Balanced

@esaurez
Enrique Saurez (esaurez) marked this pull request as ready for review October 23, 2026 03:18
@esaurez
Enrique Saurez (esaurez) merged commit 1f9e0c7 into dev Oct 23, 2026
76 checks passed
@esaurez
Enrique Saurez (esaurez) deleted the esaurez/a3-2-rooted-mappings-20261021 branch October 23, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants