Skip to content

feat(sandbox): support rootfs tar as --from source for VM driver - #2863

Open
feloy wants to merge 7 commits into
NVIDIA:mainfrom
feloy:docker-tar
Open

feat(sandbox): support rootfs tar as --from source for VM driver#2863
feloy wants to merge 7 commits into
NVIDIA:mainfrom
feloy:docker-tar

Conversation

@feloy

@feloy feloy commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add rootfs tar archive (.tar, .tar.gz, .tgz) as a new --from source for VM-driver sandboxes, enabling daemon-free sandbox creation from flat filesystem archives produced by docker export, podman export, or buildah mount + tar
  • CLI detects the archive by file extension, validates the gateway is local and uses the VM compute driver, and passes the path via driver_config
  • VM driver copies the tar into its staging area and feeds it into the existing rootfs extraction and ext4 disk creation pipeline, with mtime-based caching

Related Issue

Closes #2175

Changes

CLI (crates/openshell-cli/)

  • New ResolvedSource::RootfsTar variant in resolve_from() with extension-based detection
  • validate_rootfs_tar_source() checks local-gateway and VM-driver constraints via GetGatewayInfo RPC
  • rootfs_tar_driver_config() and merge_driver_config() encode the tar path into driver_config
  • Updated --from help text and error messages

VM driver (crates/openshell-driver-vm/)

  • rootfs_tar_path field on VmSandboxDriverConfig
  • ensure_prepared_rootfs_tar_disk() with double-checked cache locking and mtime-based identity
  • Updated validate_sandbox(), create_sandbox(), prepare_runtime_images(), and reconciliation to accept rootfs tar as an alternative to image references
  • Extracted bootstrap_image_ref_default() for reuse in rootfs tar and reconciliation paths

Docs (docs/sandboxes/manage-sandboxes.mdx)

  • Updated --from documentation with rootfs tar example and VM-driver constraint

Tests

  • 6 unit tests in run.rs: extension detection for .tar/.tar.gz/.tgz, missing archive rejection, filename_looks_like_rootfs_tar coverage
  • E2E test (e2e/rust/tests/rootfs_tar.rs): builds a Docker image, exports a flat rootfs tar, creates a VM sandbox from it, and verifies a marker file

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
  • E2E tests added/updated

Checklist

  • Follows conventional commit format
  • No proto schema changes required (driver_config carries the tar path)
  • Scoped to VM driver only — Docker and Podman drivers unaffected
  • Local-gateway constraint enforced (same as Dockerfile sources)
  • User-facing docs updated
  • DCO sign-off included

@feloy
feloy requested review from a team, derekwaynecarr, mrunalp and sjenning as code owners August 21, 2026 07:14
@copy-pr-bot

copy-pr-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gator-agent

PR Review Status

The VM rootfs-tar feature is project-valid and documented, but this head has three blocking implementation issues: the new raw host-path field crosses the driver trust boundary, the CLI encodes that field outside the required vm configuration object, and the cache key can collide or remain stale.

Action required: @feloy, please address GATOR-28b5152e-01, GATOR-28b5152e-02, and GATOR-28b5152e-03, including the requested regression coverage.

Blocking findings:

  • GATOR-28b5152e-01: caller-controlled host paths reach privileged VM-driver filesystem reads.
  • GATOR-28b5152e-02: the CLI-generated rootfs path is discarded by driver-config selection.
  • GATOR-28b5152e-03: rootfs archive cache identities are collision-prone, stale at sub-second updates, and unbounded.

Carried findings:

  • None
Gator metadata
  • Validation: Project-valid implementation of maintainer-supported issue #2175, scoped to local VM gateways.
  • Docs: Fern sandbox documentation updated for the direct UX change.
  • Checks: Current required Branch Checks and Helm Lint have not run and remain pending.
  • E2E: test:e2e is required for VM sandbox lifecycle behavior but is not dispatched while review blockers remain.
  • Head SHA: 28b5152ee0e3bc377b555cdbb89faa7cc8bc1f5c
  • Base SHA: 40d1b4866691be03b5617089739c7f2325487479
  • Merge base SHA: 40d1b4866691be03b5617089739c7f2325487479
  • Patch ID: 4f052d6ec908bc7bf57cfeb49f381929c826deee
  • Gator payload: 7
  • Review mode: initial
  • Previous reviewed SHA: none
  • Review budget exhausted: no
  • Maintainer decision required: no
  • Next state: gator:in-review

Comment thread crates/openshell-driver-vm/src/driver.rs Outdated
Comment thread crates/openshell-cli/src/run.rs Outdated
Comment thread crates/openshell-driver-vm/src/driver.rs Outdated
@johntmyers johntmyers added gator:in-review Gator is reviewing or awaiting PR review feedback gator:blocked Gator is blocked by process or repository gates and removed gator:in-review Gator is reviewing or awaiting PR review feedback gator:blocked Gator is blocked by process or repository gates labels Aug 25, 2026

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gator-agent

PR Review Status

Thanks @feloy, I checked the new staging-boundary validation against the three existing findings. The new head narrows arbitrary host-path access, but the documented CLI still copies archives into shared persistent staging before enforcing the configured limit, still encodes the rootfs path outside driver_config.vm, and leaves the cache identity and copy-consistency defects unchanged. No new blockers were added.

Action required: @feloy, please finish the three carried obligations and their requested regression coverage.

Blocking findings:

  • No new blocking findings.

Carried findings:

  • GATOR-28b5152e-01: use request-bound server-controlled staging, enforce the byte limit while receiving the archive, prevent cross-request path selection, and clean up staged data.
  • GATOR-28b5152e-02: nest rootfs_tar_path under driver_config.vm, deep-merge existing VM settings, and cover the CLI-to-driver transport.
  • GATOR-28b5152e-03: use a fixed-length collision-resistant version identity and reject or retry if the archive changes during copy.
Gator metadata
  • Validation: Project-valid implementation of maintainer-supported issue #2175, scoped to local VM gateways.
  • Docs: Fern sandbox documentation is present for the direct UX change.
  • Checks: Current-head Branch Checks and Helm Lint are pending; pipeline dispatch waits for review blockers to resolve.
  • E2E: test:e2e remains required for VM sandbox lifecycle behavior and will be dispatched after review blockers resolve.
  • Head SHA: 75e1f2be4b522e53381ab4e2a958c6dba7e031ff
  • Base SHA: 8be8b62ab58e42ee08213494b0d556ebc829e5de
  • Merge base SHA: 8be8b62ab58e42ee08213494b0d556ebc829e5de
  • Patch ID: 2c200e7c9e38781640f7fbeaa9eab2919ebce81b
  • Gator payload: 7
  • Review mode: follow_up
  • Previous reviewed SHA: 28b5152ee0e3bc377b555cdbb89faa7cc8bc1f5c
  • Review budget exhausted: no
  • Maintainer decision required: no
  • Next state: gator:in-review

@johntmyers johntmyers added gator:in-review Gator is reviewing or awaiting PR review feedback and removed gator:blocked Gator is blocked by process or repository gates labels Aug 26, 2026

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gator-agent

PR Review Status

Thanks @feloy, I checked the latest staging-limit, request-directory, and cleanup changes against the three existing obligations. The raw staging reference is still not request-bound or bounded while copying, the CLI transport and cache identity remain unchanged, and the new cleanup makes a successfully created rootfs-tar sandbox unable to restart or restore.

Action required: @feloy, please address GATOR-168b9210-01 and finish the three carried obligations with their requested regression coverage.

Blocking findings:

  • GATOR-168b9210-01: deleting the one-shot staged archive leaves persisted sandbox state pointing to a path that restart and restoration must canonicalize.

Carried findings:

  • GATOR-28b5152e-01: replace caller-selected staging paths with request-bound server-controlled staging, enforce the byte limit while receiving, and clean up every terminal path safely.
  • GATOR-28b5152e-02: nest rootfs_tar_path under driver_config.vm, deep-merge existing VM settings, and cover CLI-to-driver transport.
  • GATOR-28b5152e-03: use a fixed-length collision-resistant archive-version identity and reject or retry if the source changes during copy.
Gator metadata
  • Validation: Project-valid implementation of maintainer-supported issue #2175, scoped to local VM gateways.
  • Docs: Fern sandbox documentation remains present for the direct UX change.
  • Checks: Current-head Branch Checks and Helm Lint are pending; pipeline dispatch remains gated on review blockers.
  • E2E: test:e2e is required for VM sandbox lifecycle behavior but must not be dispatched while review blockers remain.
  • Head SHA: 168b9210cc70a99b003a18b18b2bead91d30f0b9
  • Base SHA: 8be8b62ab58e42ee08213494b0d556ebc829e5de
  • Merge base SHA: 8be8b62ab58e42ee08213494b0d556ebc829e5de
  • Patch ID: 1f7bcbdefada29ff2276cb444f62a4b8984857ae
  • Gator payload: 7
  • Review mode: follow_up
  • Previous reviewed SHA: 75e1f2be4b522e53381ab4e2a958c6dba7e031ff
  • Review budget exhausted: yes
  • Maintainer decision required: no
  • Next state: gator:in-review

Comment thread crates/openshell-driver-vm/src/driver.rs
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Maintainer Convergence Decision

Thanks @feloy. I checked the new persisted-image restoration path at head 8ec3e99844bc6061e55b6c28421af9f94a676264; it fixes GATOR-168b9210-01, and that thread is now resolved. The required critical-only delta review found no newly introduced Critical defect. Three earlier obligations remain unchanged, so a maintainer convergence decision is required before testing proceeds.

Root-cause findings:

  • GATOR-28b5152e-01: still open — staging is selected through a caller-visible path, the request directory is PID-based rather than request-bound, and the configured byte limit is not enforced while the CLI copies the archive into persistent staging.
  • GATOR-28b5152e-02: still open — the CLI still writes rootfs_tar_path at the top level and shallow-merges it, while VM driver selection consumes driver_config.vm.
  • GATOR-28b5152e-03: still open — the cache identity still uses the archive path plus a seconds-truncated mtime, without a bounded collision-resistant version key or a post-copy consistency check.
  • GATOR-168b9210-01: resolved — preserve-existing lifecycle paths now consume the persisted prepared-image identity instead of revalidating deleted request staging.

Scope growth:

  • None in this delta; it is confined to rootfs-tar restart and restoration.

Reviewer-quality signals:

  • No new Critical was proposed, and the existing obligations were carried without replacement findings.

Maintainer action: @NVIDIA/openshell-maintainers, please decide whether GATOR-28b5152e-01, GATOR-28b5152e-02, and GATOR-28b5152e-03 remain required before test dispatch; for any obligation that is acceptable as implemented, explicitly waive that finding, otherwise direct the author to complete it.

Gator metadata
  • Head SHA: 8ec3e99844bc6061e55b6c28421af9f94a676264
  • Base SHA: 8be8b62ab58e42ee08213494b0d556ebc829e5de
  • Merge base SHA: 8be8b62ab58e42ee08213494b0d556ebc829e5de
  • Patch ID: f50808f0a556f3895a7db93c521bb6dd2ee22159
  • Gator payload: 7
  • Review mode: critical_only
  • Previous reviewed SHA: 168b9210cc70a99b003a18b18b2bead91d30f0b9
  • Review budget exhausted: yes
  • Maintainer decision required: yes — three concrete prior obligations remain unresolved and unwaived
  • Next state: gator:blocked
  • Blocked reason: review_convergence_decision_required

@johntmyers johntmyers removed the gator:in-review Gator is reviewing or awaiting PR review feedback label Aug 26, 2026
@johntmyers johntmyers added gator:watch-pipeline Gator is monitoring PR CI/CD status test:e2e Requires end-to-end coverage and removed gator:blocked Gator is blocked by process or repository gates test:e2e Requires end-to-end coverage labels Aug 31, 2026
@github-actions

Copy link
Copy Markdown

Label test:e2e applied for e17ee02. Open the existing run and click Re-run all jobs to execute with the label set. The run will execute the standard E2E suite after building the required gateway and supervisor images once. The matching required CI gate status on this PR will flip green automatically once the run finishes.

@johntmyers johntmyers added gator:blocked Gator is blocked by process or repository gates and removed gator:watch-pipeline Gator is monitoring PR CI/CD status labels Aug 31, 2026
Accept flat rootfs tar archives (.tar, .tar.gz, .tgz) via the --from
flag for VM-backed gateways. The CLI detects the archive extension,
validates that the gateway uses the VM compute driver, and passes the
tar path through driver_config. The VM driver copies the tar into its
staging area and feeds it into the existing rootfs extraction and ext4
disk creation pipeline, skipping the container image pull/export steps.

Closes NVIDIA#2175

Signed-off-by: Philippe Martin <phmartin@redhat.com>
The rootfs_tar_path field in driver_config was passed from the API
caller directly to tokio::fs::copy without validation. An authenticated
user bypassing the CLI could supply arbitrary host paths (e.g.
/dev/zero for disk exhaustion, or readable host files for data
exfiltration).

Introduce a trusted staging directory that the VM driver creates on
startup and advertises via GetCapabilities. The CLI now copies the tar
into the staging directory before creating the sandbox, and the driver
validates that the received path is a regular file inside the staging
root and within a configurable size limit (default 10 GiB) before any
I/O.

New VmDriverConfig options:
- rootfs_tar_staging_dir: override the staging directory
  (default: <state_dir>/rootfs-tar-staging)
- rootfs_tar_max_bytes: override the size limit (default: 10 GiB)

Addresses GATOR-28b5152e-01.

Signed-off-by: Philippe Martin <phmartin@redhat.com>
… rootfs tar

Tighten the rootfs tar staging flow to address the remaining GATOR-01
obligations:

- Request-scoped staging: the CLI creates a unique per-request
  subdirectory (req-<pid>) under the staging root instead of placing
  files directly in the shared directory. The driver enforces that the
  tar path is at depth 2 (staging_root/<subdir>/<file>), preventing
  cross-request path selection.

- Size pre-check: the driver advertises rootfs_tar_max_bytes via
  GetCapabilities. The CLI reads this limit and rejects oversized files
  before copying, avoiding disk exhaustion in the staging directory.

- Cleanup: the driver removes the request staging subdirectory after
  consuming the tar (on cache hit, copy success, or copy failure),
  ensuring staged data does not persist beyond the request.

Signed-off-by: Philippe Martin <phmartin@redhat.com>
On restore or restart, the one-shot staged tar archive has already been
cleaned up. Reading the persisted image identity from the sandbox state
directory and resolving the cached disk path directly avoids re-accessing
the deleted staging path.

Addresses GATOR-168b9210-01.

Signed-off-by: Philippe Martin <phmartin@redhat.com>
…s tar copy

Replace PID-based request staging directories with tempfile-generated
random names to prevent collisions and make paths unpredictable.
Replace bare tokio::fs::copy with a streaming copy loop that enforces
the advertised max_bytes limit during transfer, closing the TOCTOU gap
between the pre-copy size check and the actual copy.

Signed-off-by: Philippe Martin <phmartin@nvidia.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
A caller could name any host path in `driver_config.vm.rootfs_tar_path`,
which the privileged VM driver then read. The CLI-side locality check did
not apply to direct API requests.

The gateway now owns staging. `BeginRootfsTarStaging` allocates a
request-scoped directory under the driver-advertised staging root and
returns an opaque single-use token; `CreateSandbox` carries the token, and
the gateway substitutes the path it allocated before dispatching to the
driver. `template.driver_config.<driver>.rootfs_tar_path` is rejected
outright in request validation, so a caller-supplied path never reaches
privileged I/O.

Tokens are bound to the issuing workspace and subject, consumed once, and
expire after 30 minutes. Outstanding slots are capped per caller and
overall, so one caller can neither exhaust the staging filesystem nor
starve others. An RAII guard reclaims the directory on every failure path
after consumption, and an age-gated sweep runs at startup and on each
reconcile pass for directories whose driver died before its own cleanup.

The token is stripped from the public sandbox before persistence: the
stored copy is returned verbatim by GetSandbox, ListSandboxes and
WatchSandbox to every member of the workspace.

Also fixes two defects this exposed:

- The CLI wrote `rootfs_tar_path` at the top level of `driver_config`, but
  the gateway forwards only `driver_config.<driver_name>`, silently
  dropping unmatched keys. The archive never reached the VM driver, so the
  documented `--from ./rootfs.tar` flow did not work at all. Config is now
  nested under `vm` and deep-merged, so a caller's existing VM settings
  survive instead of being clobbered by a shallow extend.

- Staging previously required `GetGatewayInfo`, which is restricted to
  `platform_admin`, making the feature unusable for ordinary users on any
  RBAC-enabled gateway. The new RPC matches CreateSandbox at
  `sandbox:write` / `workspace_role: user`.

`compute_driver.proto` is unchanged; the gateway reads the staging root
from the capabilities it already stores.

Refs NVIDIA#2175

Signed-off-by: Philippe Martin <phmartin@redhat.com>
The prepared-disk cache key combined the archive's full path with an mtime
truncated to seconds, then mapped punctuation to `-`. Distinct paths such as
`/tmp/a/b.tar` and `/tmp/a-b.tar` collapsed onto the same key and reused each
other's disk, a rewrite within the same second kept stale contents, and a long
path could exceed filesystem component limits.

Identity is now a SHA-256 of the archive contents. This is also what makes the
cache work at all now that the gateway allocates a fresh staging directory per
request: a path-derived key would miss on every create.

The archive is hashed, the cache checked, and only on a miss copied — so a hit
skips writing a multi-gigabyte file. The copy is hashed as it is written and
rejected if the digest differs from the first pass, which closes the window
where the source changes during staging rather than approximating it with a
re-stat.

Refs NVIDIA#2175

Signed-off-by: Philippe Martin <phmartin@redhat.com>
@johntmyers

Copy link
Copy Markdown
Collaborator

/ok to test 3ed0174

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gator-agent

PR Review Status

Thanks @feloy. I checked the rebased author series at head 3ed017419a0af3245d4a19d65c05988aa64c14ac against your gateway-issued staging, nested VM configuration, and content-addressed cache fixes. The range-diff preserves the four resolved Gator obligations, and the required critical-only review found no newly introduced Critical defect.

Blocking findings:

  • No blocking findings remain.

Carried findings:

  • None; GATOR-168b9210-01, GATOR-28b5152e-01, GATOR-28b5152e-02, and GATOR-28b5152e-03 remain resolved.
Gator metadata
  • Validation: Project-valid implementation of maintainer-supported issue #2175, scoped to local VM gateways.
  • Docs: Fern sandbox and gateway configuration documentation remain updated for the direct UX change and managed-driver constraint.
  • Checks: Current-head Branch Checks run 33490039963, Helm Lint run 33490039891, and Branch E2E run 33490040676 are queued or running.
  • E2E: test:e2e remains applied; /ok to test 3ed017419a0af3245d4a19d65c05988aa64c14ac created the current-head mirror and dispatched Branch E2E.
  • Head SHA: 3ed017419a0af3245d4a19d65c05988aa64c14ac
  • Base SHA: 8ffc6c2a13cd482dea9e2195058309344c274756
  • Merge base SHA: 8ffc6c2a13cd482dea9e2195058309344c274756
  • Patch ID: 4f57b99546fb06cde7c7c68a6a4213035780a994
  • Gator payload: 7
  • Review mode: critical_only
  • Previous reviewed SHA: e17ee02d5d894ad143dc3f9a3bcaaeeb704bb5df
  • Review budget exhausted: yes
  • Maintainer decision required: no — all prior findings remain resolved, there is no qualifying scope growth, and no new Critical was found
  • Review telemetry: 0 proposed findings, 0 blockers, 0 duplicate proposals, and 0 unchanged-code proposals on this head; ledger history records 4 finding-bearing rounds and 4 unique historical findings.
  • Next state: gator:watch-pipeline

@johntmyers johntmyers added gator:watch-pipeline Gator is monitoring PR CI/CD status gator:blocked Gator is blocked by process or repository gates and removed gator:blocked Gator is blocked by process or repository gates gator:watch-pipeline Gator is monitoring PR CI/CD status labels Sep 1, 2026
@feloy

feloy commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

the failing test seems flaky, is it possible to restart it?

@johntmyers johntmyers added gator:approval-needed Gator completed review; maintainer approval needed gator:blocked Gator is blocked by process or repository gates and removed gator:blocked Gator is blocked by process or repository gates gator:approval-needed Gator completed review; maintainer approval needed labels Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gator:blocked Gator is blocked by process or repository gates test:e2e Requires end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: support rootfs tar as --from source for VM driver sandboxes

2 participants