Repository navigation
feat: preserve image-declared identity in Docker and Podman #2331
Description
Activity
- addedarea:sandboxSandbox runtime and isolation workSandbox runtime and isolation work
on Jul 16, 2026 🏗️ build-plan
Implementation Plan
Issue type:
feat
Complexity: Medium
Confidence: High — the policy precedence, driver boundary, resolution
timing, workspace boundary, and non-goals are defined.Summary
Remove the requirement for a blessed
sandbox:sandboximage account.
Docker/Podman inspect and pin the final image, pass protected raw OCI
Config.User, and let the supervisor fill only policy fields that were
omitted. The result is one numeric non-root identity shared by direct and SSH
children.This PR keeps
/sandboxas the fixed OpenShell workspace. When OCI fallback is
used, OpenShell creates it if absent and chowns only the directory itself.
Existing image content and nested mounts retain their ownership. OCI
WorkingDirsupport belongs in the stacked follow-up tracked by #2526.Implementation Steps
-
Preserve omission of
run_as_userandrun_as_groupthrough policy
defaults, conversion, persistence, create, and update paths. Keep explicit
field validation unchanged. -
Add protected
OPENSHELL_OCI_IMAGE_USERmetadata and normalize supervisor
input into:enum DriverIdentity { Resolved { uid: u32, gid: u32 }, OciUser { declaration: String }, None, }
Docker/Podman select
OciUser, Kubernetes/OpenShift retainResolved, and
VM/offline retainNone. Reject conflicting or partial inputs. -
Update Docker and Podman to inspect after image selection, retain immutable
image ID plus raw/emptyConfig.User, create from that ID, keep the
supervisor at0:0, and overwrite protected identity environment. -
Add a focused supervisor resolver that applies explicit policy field, then
matching OCI component, then error. Resolve names with bounded, read-only,
no-follow account-file parsing; reject ambiguity and root. -
Store the completed numeric pair in the in-memory process policy before
readiness. Reuse the existing direct/SSH privilege-drop path and do not
rewrite account files on the OCI path. -
When OCI fallback supplies either identity component, create
/sandboxif
absent and chown only its root directory. Do not traverse existing contents,
symlinks, or nested mounts. Fail before readiness if the root cannot be
prepared. -
Add focused policy, driver, resolver, direct/SSH, and Docker/Podman E2E
coverage. Regression-test workspace content ownership, mounts, and
Kubernetes/OpenShift/VM behavior. -
Update architecture, Docker/Podman, BYOC, security, and PR walkthrough
documentation to describe the policy-first fallback and root-only workspace
preparation.
Test Plan
- Unit: omission preservation; per-field precedence; named, numeric, mixed,
missing, malformed, ambiguous, and root OCI identities; driver-input
conflicts; missing, invalid, and unpreparable workspace roots. - Integration: Docker/Podman image inspection, immutable-ID pinning,
protected environment precedence, and unchanged mount behavior. - E2E: named and passwd-less numeric images, missing/root failures, fully
explicit policy override, root-only workspace ownership, retained ownership
of existing content and nested mounts, workspace writes, and direct/SSH
identity agreement for Docker and Podman where available.
Risks & Open Questions
- Removing default normalization may expose assumptions in non-local drivers;
protect their current effective behavior with regression tests. - Account-file parser tests must remain file-based for SELinux/AppArmor
compatibility. - Identity failure is asynchronous container startup failure; report a precise
fatal error before readiness without adding a new protocol. - A fixed
/sandboxremains a temporary compatibility boundary. Follow-up
issue feat: honor OCI WorkingDir for Docker and Podman workspaces #2526 must resolve OCIWorkingDirconsistently across mounts,
filesystem policy, direct/SSH cwd, upload/download, and editor behavior.
Documentation Impact
Update relevant architecture, driver, BYOC, security, and walkthrough
documentation. No gateway configuration fields are added.
Revision 4 — keep
/sandboxfixed in #2509, prepare only its root directory,
retain existing content/mount ownership, and defer OCIWorkingDirto stacked
follow-up #2526.Revision 3 — replace fixed modes, persisted identity, gateway handshake, mount
restrictions, and complete-pair image authority with policy-first per-field OCI
fallback.Revision 2 — replace fixed
10001:10001design with OCIUSERarchitecture;
add persistence, storage boundary, policy authority, and observability.Revision 1 — initial fixed-identity plan for #2331.
-
- addedstate:review-readyReady for human reviewReady for human reviewstate:agent-readyApproved for agent implementationApproved for agent implementationand removedstate:review-readyReady for human reviewReady for human review
on Jul 16, 2026 - addedstate:in-progressWork is currently in progressWork is currently in progress
on Jul 16, 2026 🏗️ build-from-issue-agent
Implementation Complete
PR: #2335
What was built
Docker and Podman now choose a validated numeric sandbox UID/GID, inject it as protected supervisor configuration, and launch agent processes with consistent identity environment variables. A userless image fixture verifies that images no longer need a baked-in
sandboxaccount.Tests
- Unit: driver identity defaulting, overrides, range validation, and environment precedence; supervisor child identity environment behavior
- Integration: Docker and Podman
custom_imagefeature variants compile - E2E: Docker userless-image path passed locally; rootless Podman runtime validation is covered by CI because Podman is unavailable on the development host
Docs updated
- Runtime and sandbox architecture overviews
- Docker and Podman driver READMEs
- Gateway configuration reference and Docker sample
- Bring-your-own-container example
The issue will auto-close when the PR is merged.
- addedstate:pr-openedPR has been opened for this issuePR has been opened for this issueand removedstate:in-progressWork is currently in progressWork is currently in progress
on Jul 16, 2026 🏗️ build-from-issue-agent
PR blocked by vouch workflow
PR #2335 was created successfully, but the
Vouch Checkworkflow closed it twice while reportingmatthewgrossmanas unvouched. GitHub reports the author's repository association asmember; the workflow's org-membership bypass did not take effect.The implementation, signed-off commit, full pre-commit suite, and local Docker userless-image E2E are complete. To resume CI, add
matthewgrossmanto the dedicatedvouchedbranch through the normal/vouchprocess (or repair the workflow'sORG_READ_TOKENmembership check), then reopen #2335.- addedstate:in-progressWork is currently in progressWork is currently in progressstate:pr-openedPR has been opened for this issuePR has been opened for this issueand removedstate:pr-openedPR has been opened for this issuePR has been opened for this issuestate:in-progressWork is currently in progressWork is currently in progress
on Jul 16, 2026 9 remaining items
- addedstate:review-readyReady for human reviewReady for human reviewand removedstate:in-progressWork is currently in progressWork is currently in progress
on Jul 25, 2026 Implementation is ready for review in #2476.
Validation completed:
mise run pre-commit- Docker custom-image identity E2E: 4 passed
- Final principal/security review: no blocking findings
Podman E2E remains unrun locally because Podman is unavailable.
- addedstate:agent-readyApproved for agent implementationApproved for agent implementationstate:in-progressWork is currently in progressWork is currently in progressand removedstate:review-readyReady for human reviewReady for human review
on Jul 28, 2026 🏗️ build-from-issue-agent
Implementation Complete
PR: #2509
What was built
Docker and Podman now use policy-first, per-field OCI image identity fallback without requiring a baked-in
sandboxaccount. The drivers pin launch to the inspected immutable image ID, and the supervisor resolves the final numeric UID/GID before starting direct or SSH agent children.Tests
- Unit: 14 tests added or updated across policy persistence, driver launch, and identity resolution
- Integration: Full
mise run testandmise run cisuites pass - E2E: 2 focused Docker custom-image tests pass on commit
3bf0b9f6; Podman E2E unavailable because Podman is not installed on this host
Docs updated
- Compute runtime and sandbox architecture
- Docker and Podman driver guidance
- Policy schema, compute driver, policy, security, and BYOC documentation
- Related OpenShell agent skills
The issue will auto-close when the PR is merged.
- addedstate:pr-openedPR has been opened for this issuePR has been opened for this issueand removedstate:in-progressWork is currently in progressWork is currently in progress
on Jul 28, 2026 🏗️ build-from-issue-agent
Workspace scope clarified
PR #2509 now keeps
/sandbox
as the fixed compatibility workspace but no longer performs a recursive
ownership rewrite. On head
5ca344e6:- OCI identity fallback creates
/sandboxif needed and chowns only that
directory. - Existing image content, symlinks, and nested mounts are not traversed and
retain their ownership. - Both direct and SSH children can create files in the prepared workspace.
- Images only need to pre-own existing files that the agent must modify.
- Kubernetes/OpenShift and VM behavior remains unchanged.
Adopting OCI
WorkingDirand removing/sandboxas a Docker/Podman image-facing
workspace convention is intentionally deferred to a separate stacked follow-up
PR. This keeps #2509 focused on removing thesandbox:sandboxaccount
requirement.Validation on the new head:
mise run pre-commit- Supervisor unit tests: 98 passed, 1 ignored
- Focused Docker E2E: 5 passed
- Docker and Podman E2E targets compile
- OCI identity fallback creates
- added a commit that references this issue
on Jul 28, 2026 - added a commit that references this issue
on Jul 29, 2026
Problem
Docker and Podman run the OpenShell supervisor as root and replace the image
entrypoint. Agent children currently fall back to the policy identity
sandbox:sandbox, forcing otherwise compatible images to contain those exactaccounts.
Docker and Podman should instead use the image's OCI
USERfor any processidentity field omitted by policy. The supervisor remains root; only agent
children use the resolved identity.
Required behavior
sandboxremains a request for that account; omission activates OCIfallback.
app,app:staff,1234,1234:1235, and mixed forms.USER apporUSER 1234, use the passwd primary GID when group fallbackis required.
ambiguous, or resolves to UID/GID 0.
USERfails only when at least one policy field needsOCI fallback.
Design
Preserve omission of
run_as_userandrun_as_groupfor Docker and Podman.Continue materializing the legacy
sandboxdefaults for Kubernetes,OpenShift, VM, and unknown/remote drivers.
Docker and Podman inspect the final image for its immutable ID and raw OCI
Config.User, then create the container from that exact image ID.Drivers provide protected identity environment:
OPENSHELL_OCI_IMAGE_USERand clearOPENSHELL_SANDBOX_UID/GID.pair.
The supervisor normalizes that input into an internal enum:
For
OciUser, resolve only omitted policy components against bounded,read-only
/etc/passwdand/etc/groupparsing. Do not use NSS or rewriteaccount files.
Produce one non-root UID/GID pair before readiness and feed it into the
existing direct/SSH privilege-drop path.
Scope
otherwise prepare
/sandboxfor OCI-derived identities./sandboxremains the current workspace convention. Until the follow-uplands, custom images must provide a workspace usable by their selected
identity.
PVC,
fsGroup, and existing/sandboxownership behavior./sandboxinitialization.WorkingDirand dynamic workspace-root support are tracked in follow-upissue feat: honor OCI WorkingDir for Docker and Podman workspaces #2526 and will be implemented separately.
status, session handshake, readiness protocol, supervisor CLI flag, or new
public setting.
Tests
USER.behavior.
persistence behavior.
Scope update
Earlier revisions included Docker/Podman preparation of
/sandbox. That workwas removed from #2509 because it coupled identity fallback to a partial
workspace solution. Workspace behavior belongs with OCI
WorkingDirsupportin #2526.