Skip to content

feat(gpu): derive Docker sandbox requirements from CDI specs - #4204

Draft
elezar wants to merge 6 commits into
test/4460-tmachine-cdi/elezarfrom
codex/1606-cdi-policy-docker
Draft

elezar wants to merge 6 commits into
test/4460-tmachine-cdi/elezarfrom
codex/1606-cdi-policy-docker

Conversation

@elezar

@elezar elezar commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Summary

Derive Docker GPU workload access from the selected CDI specifications, so Orin and other GPU systems receive the device-node, library, socket and group access that the fixed GPU allowlist does not cover.

Stacked on #4461 (test/4460-tmachine-cdi/elezar), which is stacked on #4529 (codex/cdi-name-parser). The tmachine harness and named CDI scenario fixtures come from #4461. The upstream CDI revision, Git source allowlist and RPM vendoring belong to those prerequisites; this PR only adds the sandbox's references to existing dependencies.

Related Issue

Related to #1606 (currently closed). Supersedes #2775, #2776, and #2265. The standard-sbin PATH repair remains a separate follow-up in #2846.

Changes

The six commits are ordered for review, and each builds and passes its tests on its own:

  1. feat(core): CDI context in the boundary protocol. A versioned CDI context schema (selected device IDs and projected spec directories) carried as an optional boundary configuration field, omitted when absent. No driver sets it yet.
  2. feat(sandbox): resolve CDI requirements. Resolves the selected devices against the projected specs and derives device nodes, read-only paths (shared libraries expand to their parent directory), exact socket targets, additional GIDs, and the NVIDIA MPS pipe directory as a writable IPC directory when mounted nosuid,nodev,noexec. Effective mount access comes from the workload's mount table. Ordinary writable mounts, broad roots and conflicting uses of one path are rejected. Captured Orin, Spark and multi-GPU specs run through the production resolver against reviewed baselines. Not yet wired in, so this commit has dead-code warnings.
  3. feat(sandbox): enforce CDI-derived access. At agent start, adds the derived paths unless authored policy already covers them, validates every CDI target through the descriptor that anchors its Landlock rule at each launch and exec, and requires Landlock enforcement for CDI targets even in best-effort mode. An authored read-only entry for a device node or IPC directory fails launch instead of being overridden. Missing additionalGids produce a warning, not a failure. Enrichment, failures and the effective Landlock mode are reported as OCSF events. Sandboxes without a CDI context keep the existing GPU baseline.
  4. feat(docker): project spec directories and record the context. Records the selected devices and the spec directories the daemon currently reports, and projects them read-only, non-recursive and private under /.openshell/cdi-specs, which user mounts, image volumes and the workspace cannot overlap. Before creation, each directory is probed with a stopped container from the prepared image, bounded by a timeout and labelled for cleanup with its sandbox. Missing directories are skipped. With resource admission enabled, CDI containers must keep a device request matching the recorded selection and the recorded projections. Admission reads the bootstrap only for CDI containers, and boundary files are replaced atomically. The Docker e2e gateway enables resource admission in GPU mode.
  5. docs(gpu). Published policy and runtime docs, the cluster debugging skill and crate READMEs.
  6. test(cdi). A tmachine test that runs the device-node-with-gid fixture as non-root and requires the CDI group and read-write device access, plus MPS pipe directory fixtures (positive, and negative without noexec).

The NVIDIA Container Toolkit currently never emits the MPS pipe directory because its lookup rejects directories; NVIDIA/nvidia-container-toolkit#2143 fixes that.

Testing

  • Docker driver library suite: 161 pass, 2 live-Docker tests ignored. Covers context creation, projections, probes (including timeout and failed removal), reserved paths, and admission (device selection, bind recursion, context version, bootstrap read failures).
  • Linux sandbox suite (aarch64-unknown-linux-gnu, non-root, Landlock required): 310 pass. network_broker::tests::broker_refuses_socket_families_it_cannot_confine fails in the local container both with and without this change.
  • Each commit checked individually: workspace build; sandbox tests after commits 2 and 3; driver tests after commit 4.
  • Clippy with warnings denied on macOS and aarch64-unknown-linux-gnu for core, the Docker driver, the sandbox and the CDI feature-test crate; cargo fmt --check passes.
  • Landlock end-to-end regression: an IPC directory with a FIFO under /dev/shm is writable after enforcement while sibling files stay denied, and the test fails when the directory is only read-only.
  • Against Linux Docker Engine (Docker-in-Docker):
    • CDI additionalGids are ignored by 29.4.3 and applied by 29.5.3.
    • CDI edits are re-resolved on every container start.
    • A container whose structured bind source was removed is refused at start, without recreating the source.
    • HostConfig.Mounts reports ReadOnly and NonRecursive as configured.
    • The tmachine group-device probe passes with additionalGids and fails without.
  • Docker GPU E2E (RTX, L4, WSL) passed on an earlier revision of this branch and needs to run on this one.
  • tmachine CDI suite, including the new group-device test, not yet run.
  • Orin/IGX, Spark and SELinux-enforcing hosts not yet tested; unit and container tests use injected hardware facts and do not establish CUDA initialization.
  • mise run pre-commit not run on this revision (the local compiler-cache wrapper fails in this environment); its Rust format and lint steps were run directly.

Compatibility requirements

I accept the following compatibility changes for Docker GPU sandboxes:

  • Writable CDI mounts are rejected. Ordinary CDI file and directory mounts must be effectively read-only, and launch fails for a writable one even when an authored read_write grant names its destination or an ancestor. The only exception is the NVIDIA MPS pipe directory (/tmp/nvidia-mps) mounted nosuid,nodev,noexec. Explicit authorization for CDI host writes is not supported. Device nodes and Unix sockets are unaffected.
  • Authored read-only entries for CDI device nodes fail launch. Listing a CDI device node or the MPS pipe directory exactly in filesystem_policy.read_only stops the launch instead of leaving the GPU unusable.
  • /.openshell/cdi-specs is reserved. User mounts, image volumes and an OCI working directory that overlap it are rejected for all Docker sandboxes.
  • Missing CDI groups warn instead of failing. Docker releases before 29.5 ignore CDI additionalGids. The workload still starts, an OCSF warning names the missing groups, and devices restricted to those groups may be inaccessible until Docker is upgraded and the sandbox restarted. This replaces the earlier launch failure.
  • Stricter admission for new GPU sandboxes. With resource admission enabled, containers with a CDI context must keep the recorded device request and spec projections. Existing GPU containers without a CDI context keep legacy baseline handling on restart and revalidation.
  • Restarts depend on the recorded spec directories. Docker refuses to start a sandbox whose recorded spec directory no longer exists, for example /var/run/cdi after a reboot without CDI spec regeneration. Regenerating the specs and starting again recovers it.
  • Downgrades. GPU sandboxes created by this version carry a CDI context in their bootstrap that earlier runtimes do not recognise; recreate them after downgrading the gateway.

Checklist

  • Follows Conventional Commits.
  • Commits are signed off for DCO.
  • Relevant documentation and troubleshooting skill updated.

🤖 Generated with Claude Code

@elezar
elezar requested review from a team, derekwaynecarr, mrunalp and sjenning as code owners October 5, 2026 15:22
@elezar elezar added test:e2e Requires end-to-end coverage test:e2e-gpu Requires GPU end-to-end coverage labels Oct 5, 2026
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

Label test:e2e-gpu applied, but pull-request/4204 does not exist yet. A maintainer needs to comment /ok to test 966d8eab99d30c0615105d07379de02af8695f28 to mirror this PR. Once the mirror exists, re-apply the label or re-run Branch E2E Checks from the Actions tab.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

Label test:e2e applied, but pull-request/4204 does not exist yet. A maintainer needs to comment /ok to test 966d8eab99d30c0615105d07379de02af8695f28 to mirror this PR. Once the mirror exists, re-apply the label or re-run Branch E2E Checks from the Actions tab.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

Label test:e2e applied for 966d8ea. 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, sandbox, and supervisor images once. The matching required CI gate status on this PR will flip green automatically once the run finishes.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

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

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

@drew

drew commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

The new CDI spec bind mounts need to participate in the existing GPU trust exemption. With resource admission enabled by default, admit_container_resources() currently rejects these binds as unlabelable effective mounts, so GPU provisioning fails with OuterFenceRejected before the container starts.

We can treat GPU attachments and their driver-managed CDI spec projections as trusted. Please have admission recognize and allow the exact expected CDI mounts by validating their daemon-reported source directory, reserved workload destination, and read-only mode. Keep rejecting arbitrary host binds, including on GPU sandboxes, rather than skipping admission for the entire container.

Please also add a GPU provisioning/admission regression test with resource admission enabled; the current GPU test configuration disables it.

@drew drew 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.

Reviewed at 966d8ea. I found two validation issues, detailed inline: library-parent expansion bypasses broad-path rejection, and implicitly writable mounts skip the new opt-in checks. Please add regression cases for both.

The overall design keeps Docker responsible for device injection and resolves policy requirements inside the workload namespace.

The Docker troubleshooting skill (skills/debug-openshell-cluster/SKILL.md) also needs a companion update covering CDI projection and supplemental-group failures.

Validation: static review only; I did not run tests. CI was still running when checked, and the PR description reports Docker GPU E2E validation as pending.

Comment thread crates/openshell-core/src/cdi_linux.rs Outdated
for (path, access) in self.mount_paths {
match access {
CdiAccess::ReadOnly => {
read_only_paths.insert(shared_library_parent(&path).unwrap_or(path));

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.

Validate the derived library parent before adding it. Before this PR, GPU enrichment used a fixed allowlist. Now a read-only CDI destination such as /libexample.so passes normalize_policy_path() and then expands here to /, granting read access far beyond that library within the outer sandbox boundary. validate_cdi_requirements() only checks that the resulting directory exists, so it does not catch this. Apply the broad-path rejection to the derived parent as well, and add a regression case for a library directly beneath / (and other rejected roots).

Comment thread crates/openshell-core/src/cdi_linux.rs Outdated
Comment on lines +340 to +343
if read_write_requested {
Ok(CdiAccess::ReadWrite)
} else {
Ok(CdiAccess::ReadOnly)

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.

Do not infer read-only access from the absence of rw. This classifies options: [bind] or [rbind] as read-only, although a bind mount can preserve a writable source mount. Consequently, these mounts never reach the single-file and exact-path allowlist checks. For example, a writable CDI directory mounted at /tmp/device-data remains writable under an existing /tmp read-write policy, despite the new contract rejecting writable CDI directories. Require explicit read-only semantics or conservatively validate the mount as writable, with regression coverage for omitted ro/rw options. See Linux bind-mount semantics.

@rsliter

rsliter commented Oct 12, 2026

Copy link
Copy Markdown

Thanks for the CDI work. We have an IGX Orin JetPack 6.2 reproduction that appears relevant: the non-root workload lacks group 44 and cuInit(0) fails in both direct OpenShell and NemoClaw sandboxes, while the plain Docker control succeeds when group 44 is added.

The host reports NVIDIA Container Toolkit in CSV mode. From this PR's current diff, I understand the Docker path requires daemon-reported CDI spec directories and derives device and supplemental-group requirements from the selected spec. We're checking whether this host exposes those prerequisites; CSV mode alone does not answer that.

Is this IGX configuration within the intended scope of #4204, and would a hardware retest be useful once the PR is ready? We can provide the host's nvidia-ctk cdi list, Docker CDI spec-directory information, and relevant redacted spec entries if helpful.

@elezar

elezar commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Thanks for the CDI work. We have an IGX Orin JetPack 6.2 reproduction that appears relevant: the non-root workload lacks group 44 and cuInit(0) fails in both direct OpenShell and NemoClaw sandboxes, while the plain Docker control succeeds when group 44 is added.

The host reports NVIDIA Container Toolkit in CSV mode. From this PR's current diff, I understand the Docker path requires daemon-reported CDI spec directories and derives device and supplemental-group requirements from the selected spec. We're checking whether this host exposes those prerequisites; CSV mode alone does not answer that.

Is this IGX configuration within the intended scope of #4204, and would a hardware retest be useful once the PR is ready? We can provide the host's nvidia-ctk cdi list, Docker CDI spec-directory information, and relevant redacted spec entries if helpful.

I would say that this IGX configuration is within the intended scope of this PR. (In addition to #2846 which addresses the location of nvidia-smi on these systems). Testing to this regard (once I have updated this PR to include more recent changes off main) would be helpful.

Some things to note:

  1. The NVIDIA Container Toolkit should be generating CDI specs that include additional GIDs for the nvmap device nodes. Please confirm this in the /var/run/cdi/nvidia.yaml file that is generated on the system. This should mean that the --group-add 44 flags being added to the Docker command line are not required. This does assume that the following commit moby/moby@90deaa8 is present in the Docker version being used and has not subsequently changed on the Docker end.
  2. A closer match to what is happening in OpenShell would be to run Docker with the --device=nvidia.com/gpu=all flag instead of using the nvidia runtime and the NVIDIA_* envvars.

@krishicks krishicks 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.

🤖 This review was written by Claude (an AI assistant), working commit by commit through the PR at d87c6abb7c with @krishicks, who asked for it and is posting it. Findings come from reading the diffs against main; nothing was executed, so please verify before acting on them.

The descriptor-anchored validation in cdi_paths.rs is careful work, and the symlink-swap tests are a nice touch. Below are the issues that seem worth addressing, roughly in priority order, followed by an optional suggestion on structure.

Issues

1. CDI escalates the entire Landlock ruleset to HardRequirement, not just CDI rules (landlock.rs, prepare_with_path_open_mode)

The overridden compatibility also drives the availability probe and try_open_path for authored paths. For every CDI GPU sandbox, a missing authored path (e.g. a default-policy directory absent from the image) and a kernel/runtime without Landlock both become fatal. The docs describe this as "CDI validation and enforcement failures abort launch", which undersells it. Could this be scoped to the CDI targets only, or called out explicitly as a policy decision?

2. CDI spec-dir binds have host-side effects (DockerGpuRuntimeCapabilities::cdi_spec_bind_strings)

The projection builds "{source}:{dst}:ro,z" directly instead of going through docker_bind_string:

  • z is forced, so every GPU sandbox relabels the host's /etc/cdi / /var/run/cdi. User binds only relabel on explicit selinux_label opt-in.
  • The existence check is skipped. docker_bind_string checks existence precisely because legacy -v binds silently create missing sources. Docker commonly reports both /etc/cdi and /var/run/cdi when only one exists, so the other gets created as an empty root-owned host directory.

Relatedly, the whole spec directory (every vendor's specs) is exposed to the workload, not just the selected devices. Landlock likely keeps agents out of /run/openshell/boundary, but it would be good to confirm and note that.

3. The fixture baselines exercise a code path production doesn't use (cdi_linux.rs tests)

The three NVIDIA fixture baselines and most behavioral tests go through resolve_spec_hints, which is #[cfg(test)] and classifies access from spec ro/rw options. Production resolve_cdi_context deliberately ignores those and classifies from effective mount state. The two have already drifted: the Orin and Spark baselines still list /run/nvidia-persistenced/socket under read_only_paths, while production now classifies it as a socket (56c8376). The ~3.7k lines of fixtures didn't catch the socket case and now encode behavior production doesn't have. Suggestion: inject the mount-table/path-kind lookups so tests drive the production function, and drop the hint path.

4. The resolver lives in openshell-core, which spreads the git dependency everywhere

Only openshell-sandbox calls resolve_cdi_context, validate_cdi_requirements, and CdiMountTable. The Docker driver needs only the CdiContext / CdiSpecDirectory schema. Placing the resolver in core pulls the git-pinned CDI crate (plus jsonschema, fancy-regex, oci-spec, derive_builder, …) into every Linux consumer of core. That's why examples/*/Cargo.lock each grow by ~550 lines. Keeping only the serde schema in core and moving the resolver into openshell-sandbox would contain it.

On the dependency itself: the git pin requires the deny.toml allow-git exception, the RPM vendoring change (4cad83e), and the serialize/deserialize workaround for crate-private edit fields. Is a crates.io release of container-device-interface-rs with those fields public feasible first? That would remove all three.

5. supplemental_groups duplicates the existing identity check (ProcessPolicy, SandboxPolicyWire, boundary_server.rs)

validate_running_identity / supplementary_groups_match already verify supplementary groups, including an allow-extra mode for GPU claims. The new ProcessPolicy.supplemental_groups is never applied, only compared against getgroups(). It could be a required-GID set passed to the existing check, with no change to ProcessPolicy or the wire format. As written it is also inconsistent: cdi_requirements is deliberately kept off the wire because it's derived in the workload, but supplemental_groups, derived the same way, is added to SandboxPolicyWire. (Minor: "supplemental" vs the existing "supplementary" naming.)

6. /sys docs don't match the code, and the grant is new (default-policy.mdx, apply_cdi_requirements)

The docs place the read-only /sys grant in the paragraph about GPU sandboxes without CDI context, but only the CDI path adds /sys. Legacy enrichment doesn't, and main doesn't grant /sys today. Since a read-only grant on all of /sys is a new permission, it would help to state what in CUDA init needs it and whether a narrower set works.

Smaller items

  • FilesystemPolicy.cdi_requirements is runtime-local provenance on a struct shared with the supervisor, OPA, and all drivers, which is why seven unrelated files now set cdi_requirements: None. A sandbox-local launch context would keep it contained.
  • The broad-root list (/, /dev, /proc, /sys, /run, /usr) is hard-coded in cdi_linux.rs, cdi_paths.rs, and three doc pages. A single shared constant would help.
  • Library-parent widening still grants read-only access to directories like /etc, /home, or /root if a spec places a .so there. Specs are host-admin trusted, so this is defense in depth, but please state the trust model.
  • With the descriptor-based pass in place, the path-based type checks in core's validate_cdi_requirements (std::fs::metadata, follows symlinks) look redundant. Only the writable-file allowlist check still does work there.
  • resolve_cdi_context builds requirements with every mount read_only: false, calls mount_access() only for validation and discards the result, then rebuilds everything via RequirementAccumulator in classify_cdi_mounts. A single pass would be easier to follow.
  • Socket validation is a type sanity check rather than enforcement: Landlock FS rules don't gate connect(), as the new test shows. Worth wording the docs that way.
  • 56c8376 and d87c6ab are fixups to earlier commits and could be squashed.
  • The default-policy.mdx correction of the legacy /proc wording is a pre-existing docs-drift fix and could land independently.

Optional: a behavior-oriented split

Not a blocker, just a suggestion in case it makes iteration easier. Comparing the fixture baselines against the default policy (/usr, /lib, /etc already readable), most of the user-visible value comes from device nodes and groups. Orin needs /dev/nvmap, /dev/nvhost-*, /dev/nvgpu/igpu0/*, /dev/host1x-fence, /dev/dri/*, and GIDs [44, 104, 995]. Spark needs /dev/dri/* and GIDs [44, 993]. None of these are in the legacy baseline. Nearly all derived read-only paths are already covered, so the mount-access machinery mostly adds new fail-closed checks for default policies.

Each PR below would land a behavior change:

  1. CDI device-node access for Docker GPU sandboxes. Schema, dependency, Docker projection/context/admission, and the sandbox resolving deviceNodes with descriptor validation. Unblocks Tegra/Spark GPU sandboxes; the legacy read-only baseline stays.
  2. Require runtime-supplied CDI additionalGids through the existing identity check: fail fast with a clear error instead of inside CUDA. A natural place to work out why the runtime isn't supplying them (rsliter's IGX Orin report).
  3. CDI-derived mount access. Effective-access classification, library-parent widening, writable-file allowlist, sockets, /sys. This is where the fixtures belong. With the default policy it's mostly restrictive, so a concrete case it unblocks would help justify it.
  4. Landlock HardRequirement for CDI sandboxes. Isolates the policy decision in issue 1.

2 and 3 build on 1 and are independent of each other. If fewer PRs is preferred, folding 4 (scoped to CDI rules) into 1 would also work.

@elezar
elezar changed the base branch from main to codex/cdi-name-parser October 14, 2026 08:39
@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

Unaddressed breaking changes

Existing Docker GPU sandboxes can fail after upgrade because old containers lack newly required CDI projections. New GPU sandbox creation and policy enforcement also reject previously accepted host and policy configurations.

1. state — NOT ACKED

Docker GPU containers created before this change have no CDI projection mounts, but restart admission now requires every expected projection.

2. configuration — NOT ACKED

Docker GPU creation now bind-mounts every daemon-reported CDI spec directory and requires each source to exist and be readable.

  • Impact: Hosts where Docker reports a missing default CDI directory can create GPU sandboxes at baseline but now fail container creation.
  • Action: Preserve compatibility by tolerating absent daemon default directories or defer the requirement to a breaking train with pre-upgrade validation and migration guidance.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

3. policy — NOT ACKED

A writable CDI mount previously inherited an authored ancestor grant, but now it must be a regular file named by an exact filesystem_policy.read_write entry.

  • Impact: Existing Docker GPU policies with broad writable ancestors can now fail initial launch or later exec when their CDI specification contains a writable mount.
  • Action: Retain ancestor-grant behavior for existing policies or defer this restriction to a breaking train with a policy migration tool that adds each required exact file.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204
Review details
  • Execution: complete
  • Candidate: PR #4204 (f5e54c201f45d2f3fa02c7c8472ee7fe9bebeaac)
  • Baseline: 6e69360d0e1536d891f4abfbb60c1d90e50188c9 (6e69360d0e1536d891f4abfbb60c1d90e50188c9)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

Unaddressed breaking changes

Existing Docker GPU sandboxes can fail after upgrade, and new GPU creation rejects previously accepted daemon-directory and filesystem-policy configurations. The PR documents the stricter rules but does not preserve compatibility for this non-breaking train.

1. state — NOT ACKED

Baseline GPU containers have only a CDI device request, while candidate restart admission requires CDI projection mounts that those persisted containers lack.

2. configuration — NOT ACKED

Baseline treats nonempty Docker cdi_spec_dirs as a capability signal, while candidate bind-mounts every reported directory with missing-source creation disabled.

  • Impact: A host where Docker reports any absent default CDI directory can create GPU sandboxes at baseline but now fails container creation.
  • Action: Tolerate absent reported directories when another directory resolves the selected devices, or defer this requirement to a breaking train with pre-upgrade validation.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

3. policy — NOT ACKED

A writable CDI mount previously inherited an authored ancestor read_write grant, while candidate requires a regular file named by an exact filesystem_policy.read_write entry.

  • Impact: Existing Docker GPU policies with writable ancestor grants can fail initial launch or later exec when the selected CDI specification contains a writable mount.
  • Action: Preserve ancestor-grant behavior on this train, or introduce exact-file requirements only in a breaking train with an actionable policy migration tool.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204
Review details
  • Execution: complete
  • Candidate: PR #4204 (15be4fa563f9b501b126e0dccb73c034ca65b0d2)
  • Baseline: 360f6d5cc2ad707bf84cbcda239e08086a527cac (360f6d5cc2ad707bf84cbcda239e08086a527cac)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

Unaddressed breaking changes

Three intentional Docker GPU incompatibilities remain in persisted sandbox state, daemon CDI-directory configuration, and filesystem policy behavior. The PR author explicitly acknowledges and documents them, but this train does not allow Stable breaks.

1. state — ACKED

Baseline Docker GPU containers have only a CDI device request, while candidate start requires matching /.openshell/cdi-specs/* projection mounts.

  • Impact: After upgrade with resource admission enabled, starting an existing stopped Docker GPU sandbox fails with CDI spec projection is missing.
  • Action: Grandfather legacy GPU containers or provide a pre-upgrade migration before enforcing CDI projection admission; documented recreation alone does not make the break valid on this train.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

2. configuration — ACKED

Baseline treats any nonempty Docker cdi_spec_dirs list as CDI support, while candidate bind-mounts every reported directory with missing-source creation disabled.

  • Impact: GPU sandbox creation now fails when Docker reports an absent default CDI directory even if another reported directory contains the selected device specification.
  • Action: Ignore absent reported directories when another directory resolves the selected devices, or defer the all-directories requirement to a breaking train with pre-upgrade validation.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

3. policy — ACKED

A writable CDI destination previously inherited an authored ancestor read_write grant, while candidate requires the exact regular-file destination in filesystem_policy.read_write.

  • Impact: Existing Docker GPU policies with writable ancestor grants can fail initial launch or later exec when the selected CDI specification contains a writable mount.
  • Action: Preserve ancestor-grant behavior on this train, or introduce exact-file requirements only in a breaking train with actionable policy migration tooling.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204
Review details
  • Execution: complete
  • Candidate: PR #4204 (440a881ed56605d01dd6f1eb3e662476f2aa0f3b)
  • Baseline: 360f6d5cc2ad707bf84cbcda239e08086a527cac (360f6d5cc2ad707bf84cbcda239e08086a527cac)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

@elezar
elezar force-pushed the codex/1606-cdi-policy-docker branch from 1d218a5 to 9a7c9b5 Compare October 14, 2026 12:19
@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

Needs review

Docker GPU launches now require every CDI additionalGid to be present in the workload process. Hardware validation is still pending, so compatibility with existing Docker GPU environments is unresolved.

1. api-behavior — NOT ACKED

openshell sandbox create --gpu now rejects process launch when CDI additionalGids are absent; the baseline did not validate spec-required groups.

  • Impact: Docker or Orin runtimes that do not propagate those groups will fail initial launch and later exec instead of starting the GPU workload.
  • Action: Validate Docker GPU and Orin launches on supported hardware, then preserve baseline behavior or document a runtime configuration that supplies every required GID.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204
Review details
  • Execution: complete
  • Candidate: PR #4204 (c18237adce1ec8ef212229230e3e4c8ebb009cac)
  • Baseline: 360f6d5cc2ad707bf84cbcda239e08086a527cac (360f6d5cc2ad707bf84cbcda239e08086a527cac)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

@elezar
elezar force-pushed the codex/1606-cdi-policy-docker branch from 9a7c9b5 to b724cc8 Compare October 14, 2026 12:51
@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

Unaddressed breaking changes

Docker GPU workloads using writable ordinary CDI mounts now fail even when policy authorizes the destination. Runtime compatibility for CDI additionalGids remains unverified on supported hardware.

1. policy — ACKED

Docker GPU process launch previously accepted runtime-provided CDI mounts under authored read_write policy, but now rejects every ordinary writable CDI file or directory mount.

  • Impact: Existing GPU workloads with writable CDI mounts fail launch or exec even when the destination or an ancestor is explicitly authorized.
  • Action: Preserve authored read_write authorization for existing CDI mounts or defer this restriction to a train that allows breaks with actionable migration guidance.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

2. api-behavior — ACKED

Docker GPU launch and every later exec now require all CDI additionalGids to be present in the workload process, while the baseline did not validate spec-required groups.

  • Impact: Docker or Orin runtime combinations that do not propagate every CDI group fail process startup instead of running the GPU workload.
  • Action: Complete Docker GPU and Orin hardware validation, then preserve baseline behavior or document and verify the runtime configuration that supplies every required GID.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204
Review details
  • Execution: complete
  • Candidate: PR #4204 (a29600181815ee57dba785fdcec3abda0fbe81b4)
  • Baseline: 360f6d5cc2ad707bf84cbcda239e08086a527cac (360f6d5cc2ad707bf84cbcda239e08086a527cac)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

@elezar
elezar marked this pull request as draft October 14, 2026 13:01
@copy-pr-bot

copy-pr-bot Bot commented Oct 14, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@elezar
elezar changed the base branch from codex/cdi-name-parser to test/4460-tmachine-cdi/elezar October 14, 2026 14:40
@elezar
elezar force-pushed the codex/1606-cdi-policy-docker branch from 1b40211 to d365bb1 Compare October 14, 2026 19:43
@elezar

elezar commented Oct 14, 2026

Copy link
Copy Markdown
Member Author

/ok-to-test d365bb1

@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

Unaddressed breaking changes

Docker GPU sandboxes gain new CDI projection and policy requirements that can reject previously accepted launches. Candidate-created GPU state is also unreadable after downgrade, and the supplied no-break train does not permit these changes.

1. api-behavior — ACKED

Docker GPU creation previously submitted CDI device IDs directly, but now fails unless at least one daemon-reported CDI spec directory can be probed and projected into the workload.

  • Impact: GPU environments where Docker can inject CDI devices but OpenShell cannot bind or read the daemon's spec directories can no longer create sandboxes.
  • Action: Retain the legacy GPU path when CDI spec projections are unavailable, or defer this mandatory prerequisite to a train that permits breaks with deployment guidance.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

2. policy — ACKED

Ordinary writable CDI file or directory mounts now fail launch even when filesystem_policy.read_write authorizes the destination.

  • Impact: Existing Docker GPU workloads whose CDI specs contain writable host-backed mounts can no longer start or exec.
  • Action: Preserve authorized writable CDI mounts for this no-break train, or provide an equivalent supported authorization path before release.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

3. policy — ACKED

A CDI device node explicitly listed in filesystem_policy.read_only previously remained read-only, but now causes the entire launch to fail.

  • Impact: Existing restrictive GPU policies that intentionally keep a device node read-only can no longer launch even for workload operations that do not use that device.
  • Action: Keep explicit read-only policy authoritative without aborting launch, or move this fail-fast behavior to a train that permits breaks with migration guidance.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

4. policy — NOT ACKED

CDI filesystem targets now override landlock.compatibility: best_effort with hard_requirement for the complete authored ruleset.

  • Impact: Docker GPU launches that previously skipped inaccessible authored paths or degraded after Landlock construction failures now abort.
  • Action: Apply hard-requirement handling only to CDI-derived targets while preserving best_effort behavior for authored paths.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

5. state — ACKED

New Docker GPU bootstraps persist cdi_context, which baseline runtimes reject as an unknown BoundaryConfig field after downgrade.

Review details
  • Execution: complete
  • Candidate: PR #4204 (5df44364c80dc35c5acd99f913e0065dfb162865)
  • Baseline: 844e44f12e43e05365d4e3a3e051303de99ba8c9 (844e44f12e43e05365d4e3a3e051303de99ba8c9)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

@elezar
elezar force-pushed the test/4460-tmachine-cdi/elezar branch 2 times, most recently from e9fa277 to fb39ac1 Compare October 15, 2026 15:01
Define the shared CDI context schema: the selected CDI device IDs and
the workload-local paths at which a compute driver projects CDI spec
directories. Carry it as an optional field in the sandbox boundary
configuration, omitted from serialized output when absent so non-GPU
bootstraps stay readable by older sandbox runtimes. No driver sets it
yet.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
Resolve the selected CDI devices against the spec directories projected
into the workload and derive the filesystem requirements their edits
imply:

- device nodes, which need read-write access;
- read-only mounts, expanded to the parent directory for shared
  libraries;
- Unix sockets, kept as exact targets without filesystem grants;
- the NVIDIA MPS pipe directory, accepted as a writable IPC directory
  only when mounted nosuid, nodev and noexec;
- additional GIDs, excluding root.

Effective mount access comes from the workload's mount table rather
than spec options. Ordinary writable mounts, broad roots and
conflicting uses of one path are rejected.

Captured Orin, Spark and multi-GPU specs drive the production resolver
against reviewed baselines. The resolver is wired into launch in the
next commit.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
When the boundary configuration carries a CDI context, resolve it at
agent start and use the result instead of the hard-coded GPU baseline:

- add derived read-only paths, and read-write access for device nodes
  and IPC directories, unless the authored policy already covers them;
- reject an authored read-only entry for a device node or IPC directory
  instead of overriding it;
- validate every CDI target through the descriptor that anchors its
  Landlock rule at each launch and exec, rejecting broad-root aliases,
  type changes and changed mount access;
- require Landlock enforcement for CDI targets even when the policy is
  best effort.

Missing CDI additionalGids are reported as a warning, because some
runtimes do not apply them and devices that do not depend on them stay
usable. A required root group remains fatal. Enrichment, failures and
the effective Landlock mode are reported as OCSF events.

Sandboxes without a CDI context keep the existing GPU baseline.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
For GPU sandboxes, record the selected CDI device IDs and the spec
directories the Docker daemon currently reports in the protected
boundary configuration, and project those directories read-only under
/.openshell/cdi-specs so the sandbox runtime can resolve the same
specs. User mounts, image volumes and the workspace cannot overlap the
projection path.

Before creating the sandbox, probe each directory with a stopped
container from the prepared workload image and skip directories that
do not exist on the Docker host. Probes are bounded by a timeout,
labelled for cleanup with their sandbox, and remove their anonymous
volumes. A probe that cannot be removed no longer fails creation.

With resource admission enabled, containers with a CDI context must
keep a CDI device request matching the recorded selection and one
read-only, non-recursive, private bind per recorded directory.
Admission reads the bootstrap only for CDI containers, and boundary
files are replaced atomically so it never observes a partial write.

The Docker e2e gateway enables resource admission in GPU mode.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
Describe how Docker GPU sandboxes derive filesystem access from the
selected CDI devices, which CDI mounts are rejected, the NVIDIA MPS pipe
directory exception, missing additionalGids handling on Docker releases
before 29.5, and SELinux requirements for the projected spec
directories. Add cluster debugging entries for CDI projection,
admission, probe and restart failures, and document the CDI context in
the core, sandbox and Docker driver READMEs.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
Record the behavior of the named scenarios once filesystem access is
derived from the selected CDI specs: read-only mounts and library parents
are readable, device nodes are writable, and writable mounts, an unsafe
library parent, and an NVIDIA MPS directory without noexec are rejected.
Add scenarios for the MPS pipe directory with and without noexec.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
@elezar
elezar force-pushed the codex/1606-cdi-policy-docker branch from d365bb1 to 33350a8 Compare October 15, 2026 22:15
@elezar

elezar commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

/ok-to-test 33350a8

@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

New or updated breaking changes; see the full report for all findings.

1. api-behavior — No explicit risk acceptance found

Explicit Docker cdi_devices were passed directly to Docker, but now GPU creation requires at least one probeable CDI specification directory.

  • Impact: Existing environments can fail sandbox creation even when Docker can resolve and inject the selected CDI devices.
  • Action: Retain the legacy path when projections are unavailable or defer this prerequisite; the prior ACKED label is corrected because no comment or review accepts this risk.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

2. policy — No explicit risk acceptance found

Docker CDI paths absent from filesystem_policy changed from denied to automatically granted read or write access.

3. policy — No explicit risk acceptance found

Ordinary writable CDI mounts changed from policy-controlled launches to unconditional launch rejection.

  • Impact: Existing GPU workloads with writable CDI mounts fail even when filesystem_policy.read_write authorizes the destination or an ancestor.
  • Action: Preserve authored authorization or defer this restriction; the prior ACKED label is corrected because no comment or review accepts this risk.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

4. policy — No explicit risk acceptance found

A CDI device node in filesystem_policy.read_only changed from remaining read-only to aborting launch.

  • Impact: Existing restrictive GPU policies can no longer launch workloads that do not require write access to that device.
  • Action: Keep explicit read-only policy authoritative without aborting launch; the prior ACKED label is corrected because no comment or review accepts this risk.
  • PR: feat(gpu): derive Docker sandbox requirements from CDI specs #4204

5. state — No explicit risk acceptance found

Candidate-created Docker GPU bootstraps add cdi_context, which v0.1.4's deny-unknown-fields BoundaryConfig cannot decode after rollback.

Review details
  • Execution: complete
  • Candidate: PR #4204 (7cc4ba80d931630fb912ca643dca6d357d89d2da)
  • Baseline: fb39ac14bb625ddc57402770850077e37635d267 (fb39ac14bb625ddc57402770850077e37635d267)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

This branch has not been deployed

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

Labels

test:e2e Requires end-to-end coverage test:e2e-gpu Requires GPU end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants