Skip to content

feat: native Linux compute driver (openshell-driver-oci) #2255

Description

@ericcurtin

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:

  • 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.
  • Extend the Docker/Podman driver's heuristics further. Keeps the host dependency and only patches the current symptom (Sandbox stuck in Provisioning on macOS with Podman (libkrun): docker driver resolves host-gateway to unreachable bridge IP #1519-style detection gaps); the next unrecognized runtime reopens the same class of bug.
  • 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 OCI layout + content-addressed cache — 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 under config.state_dir (driver.rs:440-446).
  • 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).
  • RFC 0012 (Isolation Backend interface, in review, PR rfc-0012: Isolation Backend interface #2048) and RFC 0013 (native Windows via MXC, in review, PR docs(rfc): add RFC 0013 native Windows support via MXC #2071) are the closest in-flight prior art; this proposal is designed to depend on and compose with RFC 0012 rather than duplicate its scope.

Activity

  1. elezar commented on Jul 14, 2026

    @elezar
    Member

    This would be interesting. I would have the following quick questions:

    • Why bundle runc instead of allowing users to supply their own OCI-compliant low-level runtime?
    • Apart from the detection issues / possibly misaligned API (i.e. the docker API) what other problems does this solve? Does the additional maintenance and testing burden justify the benefits?
    • Would it make sense to implement this as an "External driver"?
  2. ericcurtin commented on Jul 14, 2026

    @ericcurtin
    ContributorAuthor

    This would be interesting. I would have the following quick questions:

    • Why bundle runc instead of allowing users to supply their own OCI-compliant low-level runtime?

    Agree we can make it configurable... It's just runc is by far the most deployed one docker, containerd, CRI-O, etc. Although crun that podman uses is great.

    But bundling runc make installation a bit more seamless... We could do both maybe? Bundling and configurable?

    • Apart from the detection issues / possibly misaligned API (i.e. the docker API) what other problems does this solve? Does the additional maintenance and testing burden justify the benefits?

    Performance and scale... Usability I guess (things like GPU via CDI directly), because we can tailor specifically to OpenShell usecases... FWIW, I think this is probably a more pragmatic proposal than bubblewrap... The authors of bubblewrap have even moved away from it for new solutions, like @cgwalters @alexlarsson (just tagging them to keep me honest).

    Maybe native is the wrong name, maybe it should be runc, etc.

    • Would it make sense to implement this as an "External driver"?

    Personally I wouldn't have interest in implementing if it wasn't a first class citizen, but maybe others would.

  3. cgwalters commented on Jul 14, 2026

    @cgwalters
    Contributor

    Extend the Docker/Podman driver's heuristics further. Keeps the host dependency and only patches the current symptom (Sandbox stuck in Provisioning on macOS with Podman (libkrun): docker driver resolves host-gateway to unreachable bridge

    On MacOS, it's just required to have a Linux VM which is an inherent part of this skew.

    I have a very strong opinion related to this, see https://blog.verbum.org/2026/03/23/agent-security-is-just-security/ - which is an argument against OpenShell having its own agent-specific way to run containers.

    I don't think having a new "native" driver would help here if we still need to support podman/docker - and I think we do for a very important reason - we should support a flow like docker|podman build -> run with openshell, and if we have a different container storage, that becomes an impediment.

  4. removed
    state:triage-neededOpened without agent diagnostics and needs triage
    on Jul 14, 2026
  5. ericcurtin commented on Jul 14, 2026

    @ericcurtin
    ContributorAuthor

    I think @jmabry should look at this, with a few tweaks I think it can solve the issues in:

    #1393

  6. jmabry commented on Jul 16, 2026

    @jmabry

    I think @jmabry should look at this, with a few tweaks I think it can solve the issues in:

    #1393

    Working off direct Linux primitives instead of bwrap seems fine. From #1393, the ability to support NFS mounts, rootless configuration for running on HPC nodes are the most important requirements. Be aware many HPC environments stuck on Rocky 8 so development should be against these older OS's. @ericcurtin

  7. ericcurtin commented on Jul 16, 2026

    @ericcurtin
    ContributorAuthor

    I think @jmabry should look at this, with a few tweaks I think it can solve the issues in:
    #1393

    Working off direct Linux primitives instead of bwrap seems fine. From #1393, the ability to support NFS mounts, rootless configuration for running on HPC nodes are the most important requirements. Be aware many HPC environments stuck on Rocky 8 so development should be against these older OS's. @ericcurtin

    Rocky 8 is useful information, can be done, that might need CI to ensure we stay compatible with older linux bases.

  8. changed the title [-]feat: native Linux compute driver (openshell-driver-native)[/-] [+]feat: native Linux compute driver (openshell-driver-oci)[/+] on Jul 21, 2026
  9. elezar commented on Jul 28, 2026

    @elezar
    Member

    Following up on the discussion around #2312, I think this issue should be scoped as an extension-driver-first OCI/runc implementation.

    Proposed direction:

    • Use refactor(vm): separate rootfs materialization #2388 as prerequisite design cleanup for image acquisition vs runtime-specific rootfs materialization.
    • Keep rootfs preparation inside the external OCI driver process.
    • Do not extend the gateway/driver RPC API to pass prepared rootfs artifacts.
    • The gateway should continue to pass image refs through CreateSandbox.
    • Implement openshell-driver-oci as a standalone ComputeDriver service over a protected Unix socket.
    • Use the current compute_driver.proto for v1; defer richer capability negotiation until the first implementation proves which fields are needed.

    Initial release acceptance criteria:

    • opt-in extension driver only
    • direct runc lifecycle
    • containerd/rootfs use only for image/snapshot preparation, not process execution
    • correct token and TLS callback handling
    • netns/veth/nftables isolation
    • durable local state and restart discovery
    • idempotent create/delete cleanup
    • clear rejection for unsupported features

    Non-goals for the first release:

    • rootless mode
    • GPU/CDI support
    • custom bind/volume/tmpfs mounts
    • local image builds
    • image-based supervisor injection
    • SELinux/AppArmor support
    • push-based watch
    • HA coordination
  10. github-actions commented on Aug 28, 2026

    @github-actions

    This issue has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. Comment or remove the state:stale label to keep it open.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:staleInactive item at risk of automatic closure.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions