You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
OpenShell's four compute drivers (openshell-driver-docker, openshell-driver-podman, openshell-driver-kubernetes, openshell-driver-vm) all translate OpenShell sandbox policy into a third party's API: a container engine daemon, a cluster control plane, or libkrun. Each translation seam is a place where semantics can drift from what OpenShell actually intends:
Heuristic detection instead of deterministic behavior.Sandbox stuck in Provisioning on macOS with Podman (libkrun): docker driver resolves host-gateway to unreachable bridge IP #1519 (closed) shows the docker driver's uses_host_gateway_alias() fingerprinting Docker Desktop, Colima, Lima, Rancher Desktop, and OrbStack by daemon OS string/hostname/labels, with no pattern for a Podman-backed Docker-compatible API on macOS — host.openshell.internal resolved to an unreachable bridge IP and sandboxes stuck in Provisioning. This is a structural cost, not a one-off bug: the "Docker-compatible API" is several different runtimes with divergent networking realities that need continual fingerprinting.
Host dependency for local/default use. Every local OpenShell run currently starts with "install Docker or Podman first." feat: support bubblewrap and Apptainer sandbox drivers for rootless/NFS hosts #1393 (open, HPC-focused) independently identifies this as a blocker in locked-down environments: enterprise policies that forbid new daemons, rootless Podman's NFS//etc/subuid friction, and virtualization overhead where it isn't wanted.
Isolation enforcement is split across a translation boundary. The driver approximates policy through engine flags/pod specs/libkrun config, and the in-sandbox supervisor patches the rest at runtime. There is ongoing work (RFC 0012, in review) to make the supervisor's boundary-construction logic a pluggable IsolationBackend contract precisely because embedding it inline couples policy enforcement to whichever engine happens to be underneath, and blocks restricted/multi-tenant clusters that reject the elevated capabilities inline construction needs (feat: Support restricted SecurityContextConstraints for managed Kubernetes platforms #899).
Proposed Design
Add a fifth compute driver, openshell-driver-native, that constructs the sandbox from kernel primitives directly instead of delegating to an external engine. Scope, informed by what the codebase already has:
Isolation via the kernel, not a re-implementation of one. Use Linux namespaces (user, mount, PID, net, UTS, IPC, cgroup), cgroups v2 for the CPU/memory limits the gateway already accepts, and seccomp + no_new_privs. Generate an OCI runtime-spec config.json from policy and drive a bundled runc through the standard create/start/state/delete CLI contract — the same integration pattern containerd/CRI-O/Podman already use — rather than hand-rolling namespace/pivot_root orchestration or linking a runtime in-process (forking inside the gateway/driver's own multithreaded async process is a hazard regardless of which runtime is chosen, so every option here ends up out-of-process). runc was chosen over youki (Rust-native, faster per its own benchmarks, but far less production exposure and security review) because the isolation-critical path is the wrong place to prefer toolchain uniformity over the OCI reference implementation's much larger adversarial track record; the differentiated work here is mapping OpenShell policy directly onto primitives, not re-debugging a decade of container-runtime edge cases.
Isolation logic implemented as an IsolationBackend, not duplicated supervisor code. RFC 0012 explicitly anticipates a component implementing both the compute-driver and isolation-backend roles together (its "Extend the compute-driver contract" alternative calls this "natural for topologies such as MXC"). openshell-driver-native should be the Linux-native counterpart to that pattern rather than adding another one-off namespace-construction branch inside the supervisor — this is a hard dependency on RFC 0012 landing, not an optional nice-to-have. Doing anything else recreates exactly the coupling problem RFC 0012 was written to close.
Reuse the VM driver's OCI image and network machinery instead of rebuilding it.openshell-driver-vm already downloads registry layers into a valid OCI layout with a content-addressed cache under its state directory (crates/openshell-driver-vm/src/driver.rs), and already generates a per-sandbox nftables ruleset with NAT, default-deny forwarding, and an input chain scoped to the gateway port (crates/openshell-driver-vm/src/nft_ruleset.rs) — today gated behind the QEMU/GPU-passthrough path only. A native driver should extract the OCI-pull/cache logic into a shared crate and reuse it with overlayfs assembly in place of ext4 conversion, and generalize the existing nftables ruleset generator to a veth-based default path instead of writing either from scratch.
GPU via CDI directly. Apply CDI device nodes/mounts from the local CDI inventory when constructing the mount namespace, the same source Docker/Podman already read from — no translation layer needed.
Process model matching the existing driver pattern.ComputeDriver is a gRPC service (proto/compute_driver.proto), not an in-process trait; drivers are separate processes. Follow the openshell-driver-vm precedent exactly: gateway spawns the driver binary over a private Unix socket, passing its own PID so the driver can validate the peer (crates/openshell-server/src/compute/vm.rs).
Rootless by default. User namespaces with newuidmap, no root requirement, no long-running daemon.
Alternatives Considered
feat: support bubblewrap and Apptainer sandbox drivers for rootless/NFS hosts #1393 (bubblewrap/Apptainer daemonless driver). Solves a related but distinct problem: unprivileged HPC hosts where installing any new tooling is restricted, using existing external unprivileged sandboxing tools rather than OpenShell owning the runtime. That approach trades control (thinner policy mapping, external tool's isolation semantics) for zero new binary footprint. This proposal is a different point on the same spectrum — full ownership of the primitives for the default local/single-node experience — and the two are complementary, not competing; a locked-down-HPC driver and a flagship native driver can both exist.
Do nothing / rely on Docker or Podman indefinitely. Keeps the ecosystem debuggability (docker ps, docker logs) and defers the engineering cost, at the price of a permanent host dependency and translated-not-constructed policy enforcement.
Non-goals
Replacing the Docker, Podman, Kubernetes, or VM drivers. Kubernetes and container-engine drivers remain the right choice for existing infrastructure; this targets the local/default experience.
Reimplementing an OCI runtime from scratch. This should bundle and drive an existing runtime (runc) through its standard CLI contract.
Windows support — that is RFC 0013 (native Windows via MXC), which is pursuing the same "no in-sandbox supervisor binary" direction for a different kernel.
Agent Investigation
Grounded against origin/main:
uses_host_gateway_alias() — crates/openshell-driver-docker/src/lib.rs:2456-2486; no Podman pattern; feeds docker_gateway_route_for_host (lib.rs:2420-2442) and docker_extra_hosts (lib.rs:2488-2499).
VM driver rootfs assembly — layers extracted via extract_layer_blob_to_dir (driver.rs:3795) and converted to ext4 via crates/openshell-driver-vm/src/rootfs.rs:83-129.
VM driver nftables ruleset (NAT + default-deny + gateway-port-only input) — crates/openshell-driver-vm/src/nft_ruleset.rs:18-61 — currently wired only through setup_tap_networking (crates/openshell-driver-vm/src/runtime.rs:402), called only from the QEMU/GPU path (runtime.rs:77-131); the default libkrun path uses a gvproxy socket instead and does not install this ruleset (runtime.rs:654-813, crates/openshell-driver-vm/src/lifecycle.rs:109-111).
Gateway-spawns-driver-as-subprocess-with-peer-PID-validation pattern — crates/openshell-server/src/compute/vm.rs:452-489; enforced via crates/openshell-driver-vm/src/main.rs:269-271 and main.rs:426-448.
ComputeDriver is tonic-generated from proto/compute_driver.proto:18-43, not a hand-written Rust trait; concrete implementations are separate processes (e.g. crates/openshell-driver-podman/src/grpc.rs:32-152).
Problem Statement
OpenShell's four compute drivers (
openshell-driver-docker,openshell-driver-podman,openshell-driver-kubernetes,openshell-driver-vm) all translate OpenShell sandbox policy into a third party's API: a container engine daemon, a cluster control plane, or libkrun. Each translation seam is a place where semantics can drift from what OpenShell actually intends:uses_host_gateway_alias()fingerprinting Docker Desktop, Colima, Lima, Rancher Desktop, and OrbStack by daemon OS string/hostname/labels, with no pattern for a Podman-backed Docker-compatible API on macOS —host.openshell.internalresolved to an unreachable bridge IP and sandboxes stuck inProvisioning. This is a structural cost, not a one-off bug: the "Docker-compatible API" is several different runtimes with divergent networking realities that need continual fingerprinting./etc/subuidfriction, and virtualization overhead where it isn't wanted.IsolationBackendcontract precisely because embedding it inline couples policy enforcement to whichever engine happens to be underneath, and blocks restricted/multi-tenant clusters that reject the elevated capabilities inline construction needs (feat: Support restricted SecurityContextConstraints for managed Kubernetes platforms #899).Proposed Design
Add a fifth compute driver,
openshell-driver-native, that constructs the sandbox from kernel primitives directly instead of delegating to an external engine. Scope, informed by what the codebase already has:Isolation via the kernel, not a re-implementation of one. Use Linux namespaces (user, mount, PID, net, UTS, IPC, cgroup), cgroups v2 for the CPU/memory limits the gateway already accepts, and seccomp +
no_new_privs. Generate an OCI runtime-specconfig.jsonfrom policy and drive a bundledruncthrough the standardcreate/start/state/deleteCLI contract — the same integration pattern containerd/CRI-O/Podman already use — rather than hand-rolling namespace/pivot_rootorchestration or linking a runtime in-process (forking inside the gateway/driver's own multithreaded async process is a hazard regardless of which runtime is chosen, so every option here ends up out-of-process).runcwas chosen over youki (Rust-native, faster per its own benchmarks, but far less production exposure and security review) because the isolation-critical path is the wrong place to prefer toolchain uniformity over the OCI reference implementation's much larger adversarial track record; the differentiated work here is mapping OpenShell policy directly onto primitives, not re-debugging a decade of container-runtime edge cases.Isolation logic implemented as an
IsolationBackend, not duplicated supervisor code. RFC 0012 explicitly anticipates a component implementing both the compute-driver and isolation-backend roles together (its "Extend the compute-driver contract" alternative calls this "natural for topologies such as MXC").openshell-driver-nativeshould be the Linux-native counterpart to that pattern rather than adding another one-off namespace-construction branch inside the supervisor — this is a hard dependency on RFC 0012 landing, not an optional nice-to-have. Doing anything else recreates exactly the coupling problem RFC 0012 was written to close.Reuse the VM driver's OCI image and network machinery instead of rebuilding it.
openshell-driver-vmalready downloads registry layers into a valid OCI layout with a content-addressed cache under its state directory (crates/openshell-driver-vm/src/driver.rs), and already generates a per-sandbox nftables ruleset with NAT, default-deny forwarding, and an input chain scoped to the gateway port (crates/openshell-driver-vm/src/nft_ruleset.rs) — today gated behind the QEMU/GPU-passthrough path only. A native driver should extract the OCI-pull/cache logic into a shared crate and reuse it with overlayfs assembly in place of ext4 conversion, and generalize the existing nftables ruleset generator to a veth-based default path instead of writing either from scratch.GPU via CDI directly. Apply CDI device nodes/mounts from the local CDI inventory when constructing the mount namespace, the same source Docker/Podman already read from — no translation layer needed.
Process model matching the existing driver pattern.
ComputeDriveris a gRPC service (proto/compute_driver.proto), not an in-process trait; drivers are separate processes. Follow theopenshell-driver-vmprecedent exactly: gateway spawns the driver binary over a private Unix socket, passing its own PID so the driver can validate the peer (crates/openshell-server/src/compute/vm.rs).Rootless by default. User namespaces with
newuidmap, no root requirement, no long-running daemon.Alternatives Considered
docker ps,docker logs) and defers the engineering cost, at the price of a permanent host dependency and translated-not-constructed policy enforcement.Non-goals
runc) through its standard CLI contract.Agent Investigation
Grounded against
origin/main:uses_host_gateway_alias()—crates/openshell-driver-docker/src/lib.rs:2456-2486; no Podman pattern; feedsdocker_gateway_route_for_host(lib.rs:2420-2442) anddocker_extra_hosts(lib.rs:2488-2499).crates/openshell-driver-vm/src/driver.rs:4293-4353(layout writers),driver.rs:3623-3746(digest-verified blob download),driver.rs:4273-4390(cache root/keying), rooted underconfig.state_dir(driver.rs:440-446).extract_layer_blob_to_dir(driver.rs:3795) and converted to ext4 viacrates/openshell-driver-vm/src/rootfs.rs:83-129.crates/openshell-driver-vm/src/nft_ruleset.rs:18-61— currently wired only throughsetup_tap_networking(crates/openshell-driver-vm/src/runtime.rs:402), called only from the QEMU/GPU path (runtime.rs:77-131); the default libkrun path uses agvproxysocket instead and does not install this ruleset (runtime.rs:654-813,crates/openshell-driver-vm/src/lifecycle.rs:109-111).crates/openshell-server/src/compute/vm.rs:452-489; enforced viacrates/openshell-driver-vm/src/main.rs:269-271andmain.rs:426-448.ComputeDriveris tonic-generated fromproto/compute_driver.proto:18-43, not a hand-written Rust trait; concrete implementations are separate processes (e.g.crates/openshell-driver-podman/src/grpc.rs:32-152).