Skip to content

feat: --volume bind mounts with auto userns-remap on rootless podman#2

Merged
vessux merged 8 commits into
mainfrom
feat/cli-volume-flag
May 21, 2026
Merged

feat: --volume bind mounts with auto userns-remap on rootless podman#2
vessux merged 8 commits into
mainfrom
feat/cli-volume-flag

Conversation

@vessux
Copy link
Copy Markdown
Owner

@vessux vessux commented May 21, 2026

Summary

Adds openshell sandbox create --volume <HOST>:<CONTAINER>[:ro] for bind mounts. Repeatable flag. Driver coverage: podman (libpod bind mounts + auto userns-remap), docker (-v passthrough), vm (early reject with clear error).

Auto userns-remap on rootless podman: when any --volume is set, the driver inspects the sandbox image's Config.User directive (fallback to community-image default 1000660000) and sets userns: keep-id:uid=N,gid=N, so bind file ownership is bidirectional between host caller and container sandbox user.

This is the fork-side prerequisite for openlock v0.6.0 (mount system v2). Ship as fork v0.4.0 via coordinated Pattern B RC cycle with openlock v0.6.0.

Related Issue

Closes the fork-side scope of the openlock-driven mount system v2 feature. See openlock spec docs/superpowers/specs/2026-05-21-bind-mount-design.md and Plan A docs/superpowers/plans/2026-05-21-bind-mount-fork.md (both in the openlock repo).

Changes

  • crates/openshell-cli/: --volume clap arg, volume_spec.rs parser, plumbed through run::sandbox_create to the gRPC SandboxSpec.volumes field.
  • proto/openshell.proto + proto/compute_driver.proto: new BindVolume message; SandboxSpec.volumes (tag 11) and DriverSandboxSpec.volumes (tag 11).
  • crates/openshell-server/src/compute/mod.rs: maps public BindVolume to driver BindVolume in driver_sandbox_spec_from_public.
  • crates/openshell-driver-podman/: emits libpod bind mount entries; new PodmanClient::image_user inspects image Config.User; auto userns: keep-id:uid=N,gid=N when volumes present. pub const COMMUNITY_SANDBOX_UID: u32 = 1_000_660_000.
  • crates/openshell-driver-docker/: extends build_binds with user volumes (host:container[:ro], no ,z SELinux suffix).
  • crates/openshell-driver-vm/: rejects bind volumes early via validate_vm_sandbox.

Testing

  • 60+ tests on podman driver, 47+ on docker, 79+ on vm. All passing.
  • New crates/openshell-cli/tests/sandbox_create_volume_e2e.rs (gated behind --features e2e) covers host-bind round trip + ro write rejection. Requires a live podman daemon; not run in unit CI.
  • mise run pre-commit passes (fmt, clippy, license, helm, markdown all green). python:proto task skipped — pre-existing env issue (missing grpc_tools in venv), unrelated to this change.

Checklist

  • Follows Conventional Commits
  • mise run pre-commit passes (Rust checks: fmt, clippy, license — all clean)
  • Unit tests added/updated (60+ podman, 47+ docker, 79+ vm, 33 CLI parser)
  • E2E tests added/updated (sandbox_create_volume_e2e.rs, gated behind --features e2e)
  • Architecture docs updated (architecture/compute-runtimes.md, crates/openshell-driver-podman/README.md, docs/sandboxes/manage-sandboxes.mdx)
  • Commits are signed off (DCO)

vessux added 8 commits May 21, 2026 12:17
Register a repeatable `--volume <HOST>:<CONTAINER>[:ro]` clap arg on
`SandboxCommands::Create`. Adds an `#[allow(clippy::large_enum_variant)]`
to suppress the size-difference lint that fires now that Create has
multiple Vec fields.

The new field is destructured as `_volumes` in the dispatch match arm;
plumbing through the create flow is deferred to a follow-up task.
…y allow

- Replace dead openshell_bin() fallback with env!("CARGO_BIN_EXE_openshell")
  so a missing binary fails at compile time rather than panicking at runtime
  with a space-in-path exec error.
- Strengthen the three parse-acceptance tests: assert exit code == 1 (no
  gateway configured) rather than != 2, pinning the exact failure mode and
  ruling out silent success (exit 0) as well as clap errors (exit 2).
- Drop OPENSHELL_ENDPOINT env var from parse tests; the "No active gateway"
  runtime check fires first (exit 1) before any network I/O.
- Add module-level doc comment explaining why subprocess is retained over
  in-process (Cli is private; run::sandbox_create bypasses clap).
- Verify large_enum_variant clippy allow: lint fires at 297 bytes (Create)
  vs 78 bytes (next variant). Update comment to cite actual sizes instead
  of "by design".
Introduces volume_spec module with BindVolumeSpec, VolumeParseError, and
parse_volume_spec, validated by 7 unit tests covering both happy paths and
all error variants (bad field count, bad ro token, non-absolute/missing host,
non-absolute container).
Add BindVolume message to openshell.proto (SandboxSpec.volumes = 11)
and compute_driver.proto (DriverSandboxSpec.volumes = 11). Wire parsed
BindVolumeSpec entries from the CLI handler through run::sandbox_create
into the CreateSandboxRequest, and map them to DriverBindVolume in
driver_sandbox_spec_from_public on the server side. Fix the docker
driver test fixture and integration test call sites for the updated
sandbox_create signature.
…sent

Append a libpod bind-mount entry for each DriverSandboxSpec.volume, and
set userns=keep-id:uid=N,gid=N (from the image USER directive) when any
volumes are present. Falls back to uid/gid 1000660000 for unset or
non-numeric USER directives.
…BOX_UID

Extract the magic number 1_000_660_000 into a named pub constant
COMMUNITY_SANDBOX_UID so all four usage sites share a single source
of truth with a doc-comment linking to the Dockerfile origin.

Fix a split-uid bug in image_user: when Config.User is ":1000", the
uid field is "" which fails to parse as u32. Previously gid was still
extracted from parts[1], producing a mismatched (1_000_660_000, 1000)
pair. Now the function returns the fallback pair immediately on any
uid parse failure, matching the documented contract.

Expand the image_user doc-comment to cover the uid=0/root case
(keep-id:uid=0,gid=0 is harmless on rootless Podman), the empty-user
case, and the non-numeric-user case.

Add an rbind/read-write assertion on bind_mounts[0] in the existing
build_container_spec_emits_bind_mount_entries test.
Extend DockerDriver's build_binds to append user-declared bind volumes
from sandbox.spec.volumes using host:container[:ro] format. Extend
validate_vm_sandbox to reject requests that carry any volumes with
Status::invalid_argument.
Add two e2e tests for `openshell sandbox create --volume` (rw round-trip
and ro write-block) gated behind the new `e2e` Cargo feature. Add bind-volume
section to the podman driver README documenting auto userns-remap. Add
`--volume` flag documentation and userns-remap note to the user-facing
manage-sandboxes page and the compute-runtimes architecture doc.
@vessux vessux merged commit d6fb385 into main May 21, 2026
10 of 12 checks passed
@vessux vessux deleted the feat/cli-volume-flag branch May 21, 2026 17:05
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.

1 participant