Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions .agents/skills/debug-openshell-cluster/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,9 +577,32 @@ kubectl -n <sandbox-namespace> logs <sandbox-pod> -c openshell-supervisor-networ
Use the VM driver logs and host diagnostics available in the user's environment. Verify:

- The VM driver process is running and reachable by the gateway.
- The runtime rootfs exists and matches the expected architecture.
- Host virtualization support is enabled.
- The sandbox supervisor can establish its callback connection to the gateway.
- The custom kernel and runtime rootfs exist and match the expected architecture.
- Host virtualization support is enabled and the active process can read and write `/dev/kvm`.
- The native host supervisor can establish its callback connection to the gateway.
- The guest process leaf accepts the authenticated virtio-vsock control channel.

For the managed libkrun driver, inspect the per-sandbox host-supervisor and
guest-console logs separately:

```bash
rg -n 'vm|grpc_endpoint|guest_tls|state_dir' .cache/gateway-vm/gateway.toml
stat /dev/kvm
id
find /tmp/openshell-vm-driver-*/sandboxes -name 'supervisor*.log' -o -name 'rootfs-console.log'
tail -n 200 /tmp/openshell-vm-driver-*/sandboxes/*/supervisor.err.log
tail -n 200 /tmp/openshell-vm-driver-*/sandboxes/*/rootfs-console.log
```

The historical `guest_tls_*` configuration fields are host-supervisor mTLS
paths and must never appear in the guest image. `grpc_endpoint` is also
host-reachable; loopback is valid. A successful guest boot logs
`VM process supervisor leaf listening on vsock port ...`. libkrun needs KVM but
does not need `CAP_NET_ADMIN`. QEMU/VFIO uses TAP and host nftables and still
requires the corresponding networking and device privileges. If the account is
listed in the group that owns `/dev/kvm` but `id` does not show that group, use
`mise run gateway:vm` or the VM e2e runner; they re-exec through the configured
group without sudo.

Then run:

Expand Down
17 changes: 9 additions & 8 deletions .github/workflows/driver-vm-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
options: --privileged
volumes:
- /var/run/docker.sock:/var/run/docker.sock
env:
MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENSHELL_IMAGE_TAG: ${{ inputs['image-tag'] }}
Expand All @@ -141,8 +143,12 @@ jobs:
cache-directories: .cache/sccache
cache-targets: "true"

- name: Install zstd
run: apt-get update && apt-get install -y --no-install-recommends zstd && rm -rf /var/lib/apt/lists/*
- name: Install zstd and verify Docker
run: |
apt-get update
apt-get install -y --no-install-recommends zstd
rm -rf /var/lib/apt/lists/*
docker info

- name: Download kernel runtime tarball
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
Expand All @@ -168,15 +174,10 @@ jobs:
- name: Verify embedded driver inputs
run: |
set -euo pipefail
for file in libkrun.so.zst libkrunfw.so.5.zst gvproxy.zst umoci.zst openshell-sandbox.zst; do
for file in libkrun.so.zst libkrunfw.so.5.zst gvproxy.zst umoci.zst openshell-sandbox.zst openshell-runtime.tar.zst; do
test -s "target/vm-runtime-compressed/${file}"
done

- name: Scope workspace to driver-vm crates
run: |
set -euo pipefail
sed -i 's|members = \["crates/\*"\]|members = ["crates/openshell-driver-vm", "crates/openshell-core"]|' Cargo.toml

- name: Patch workspace version
if: ${{ inputs['cargo-version'] != '' }}
run: |
Expand Down
25 changes: 18 additions & 7 deletions .github/workflows/driver-vm-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ jobs:
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
env:
MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENSHELL_IMAGE_TAG: ${{ inputs['image-tag'] }}
Expand All @@ -100,8 +102,12 @@ jobs:
cache-directories: .cache/sccache
cache-targets: "true"

- name: Install zstd
run: apt-get update && apt-get install -y --no-install-recommends zstd && rm -rf /var/lib/apt/lists/*
- name: Install zstd and verify Docker
run: |
apt-get update
apt-get install -y --no-install-recommends zstd
rm -rf /var/lib/apt/lists/*
docker info

- name: Build bundled supervisor
run: |
Expand All @@ -116,7 +122,9 @@ jobs:
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: driver-vm-supervisor-arm64
path: target/vm-runtime-compressed/openshell-sandbox.zst
path: |
target/vm-runtime-compressed/openshell-sandbox.zst
target/vm-runtime-compressed/openshell-runtime.tar.zst
retention-days: 1

build-driver-vm-macos:
Expand Down Expand Up @@ -180,12 +188,13 @@ jobs:
run: |
set -euo pipefail
test -f target/vm-runtime-compressed-macos/openshell-sandbox.zst
ls -lh target/vm-runtime-compressed-macos/openshell-sandbox.zst
test -f target/vm-runtime-compressed-macos/openshell-runtime.tar.zst
ls -lh target/vm-runtime-compressed-macos/openshell-{sandbox,runtime.tar}.zst

- name: Verify embedded driver inputs
run: |
set -euo pipefail
for file in libkrun.dylib.zst libkrunfw.5.dylib.zst gvproxy.zst umoci.zst openshell-sandbox.zst; do
for file in libkrun.dylib.zst libkrunfw.5.dylib.zst gvproxy.zst umoci.zst openshell-sandbox.zst openshell-runtime.tar.zst; do
test -s "target/vm-runtime-compressed-macos/${file}"
done

Expand All @@ -203,14 +212,16 @@ jobs:
.

- name: Verify packaged binary shape
run: test -x out/openshell-driver-vm
run: |
test -x out/openshell-driver-vm
test -x out/openshell-sandbox

- name: Package binary
run: |
set -euo pipefail
mkdir -p artifacts
tar -czf artifacts/openshell-driver-vm-aarch64-apple-darwin.tar.gz \
-C out openshell-driver-vm
-C out openshell-driver-vm openshell-sandbox
ls -lh artifacts/

- name: Upload artifact
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ These pipelines connect skills into end-to-end workflows. Individual skill files
| `crates/openshell-server/` | Gateway server | Control-plane API, sandbox lifecycle, auth boundary |
| `crates/openshell-sandbox/` | Sandbox runtime | Container supervision, policy-enforced egress routing |
| `crates/openshell-isolation/` | Isolation backend contract | RFC 0012 `IsolationBackend` trait + types; the supervisor-facing runtime contract for the boundary |
| `crates/openshell-isolation-vm/` | VM isolation transport | Shared authenticated host/guest boundary transport and portable process leaf for VM drivers |
| `crates/openshell-policy/` | Policy engine | Filesystem, network, process, and inference constraints |
| `crates/openshell-router/` | Privacy router | Privacy-aware LLM routing |
| `crates/openshell-bootstrap/` | Gateway metadata | Gateway registration metadata, auth token storage, mTLS bundle storage |
Expand All @@ -51,7 +52,7 @@ These pipelines connect skills into end-to-end workflows. Individual skill files
| `crates/openshell-driver-kubernetes/` | Kubernetes compute driver | In-process `ComputeDriver` backend for K8s sandbox pods |
| `crates/openshell-driver-docker/` | Docker compute driver | In-process `ComputeDriver` backend for local Docker sandbox containers |
| `crates/openshell-driver-podman/` | Podman compute driver | In-process `ComputeDriver` backend for local Podman sandbox containers |
| `crates/openshell-driver-vm/` | VM compute driver | Standalone libkrun-backed `ComputeDriver` subprocess (embeds its own rootfs + runtime) |
| `crates/openshell-driver-vm/` | VM compute driver | Standalone libkrun-backed `ComputeDriver` with a native host supervisor and embedded guest runtime |
| `crates/openshell-prover/` | Policy prover | Policy verification and proof generation |
| `crates/openshell-server-macros/` | Server macros | Compile-time helpers for gateway RPC authorization |
| `crates/openshell-supervisor-middleware/` | Middleware runtime | Generic middleware registry, remote service integration, and chain execution |
Expand Down
27 changes: 25 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion architecture/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ OpenShell builds these main artifacts:
| Gateway container image | `deploy/docker/Dockerfile.gateway` |
| Supervisor container image | `deploy/docker/Dockerfile.supervisor` |
| Helm chart | `deploy/helm/openshell` |
| VM driver/runtime assets | `crates/openshell-driver-vm` |
| VM driver/runtime assets | `crates/openshell-driver-vm` plus shared transport in `crates/openshell-isolation-vm` |
| Published docs site | `docs/` rendered by Fern config in `fern/` |

Sandbox community images are built outside this repository.
Expand Down Expand Up @@ -173,6 +173,16 @@ Runtime layout:
enforcement. The VM driver bundles its own supervisor build
(`tasks/scripts/vm/build-supervisor-bundle.sh`) and does not read
`SUPERVISOR_LIBC`.
before publishing artifacts. On Linux, the driver can materialize the
same-target embedded `openshell-sandbox` as its native host supervisor. The
macOS VM-driver archive includes a native `openshell-sandbox` sibling because
the embedded guest leaf is a Linux binary.
- **Supervisor**: Alpine base with `nftables`, static musl binary at
`/openshell-sandbox`. Static linkage keeps the binary usable when the image
is mounted or extracted into sandbox environments. The VM bundle build also
packages the Linux process leaf and a driver-controlled network-helper
runtime into the guest bootstrap while the logical VM supervisor runs
natively on the host.

Gateway image builds bake the corresponding supervisor image tag into the
gateway binary so Docker sandboxes do not depend on `:latest` by default.
Expand Down
32 changes: 24 additions & 8 deletions architecture/compute-runtimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Compute runtimes create, stop, start, delete, and watch sandbox workloads for th
gateway. They do not replace sandbox policy enforcement. Every runtime starts a
workload that runs the `openshell-sandbox` supervisor, and the supervisor
enforces the sandbox contract locally.
Compute runtimes create, stop, delete, and watch sandbox workloads for the
gateway. They do not replace sandbox policy enforcement. Container runtimes run
the logical `openshell-sandbox` supervisor in the workload boundary. VM runtimes
may instead run it on the host and use an authenticated process leaf inside the
guest. In both placements the logical supervisor owns policy and the gateway
session.

## Driver Contract

Expand All @@ -16,6 +22,9 @@ Each runtime receives a sandbox spec from the gateway and is responsible for:
- Forwarding the exact canonical main-process argv and TTY mode without shell
reconstruction. The sandbox-level environment and policy workspace apply to
the main process.
- Injecting sandbox identity and the admitted topology descriptor.
- Supplying TLS or secret material only to the logical supervisor placement.
- Providing the logical supervisor and any boundary-local process leaf.
- Reporting lifecycle and platform events back to the gateway.
- Cleaning up runtime-owned resources.

Expand Down Expand Up @@ -207,6 +216,8 @@ delete, reconciliation removes the row; otherwise it can remain `Deleting`.
| Kubernetes | Cluster deployment through Helm. | Pod plus nested sandbox namespace. | Uses Kubernetes API objects, service accounts, secrets, PVC-backed workspace storage, and GPU resources. |
| VM | Experimental microVM isolation. | Per-sandbox libkrun VM. | Managed endpoint-backed driver. The gateway spawns `openshell-driver-vm`, waits for its Unix socket, and then consumes it through the same remote `compute_driver.proto` path used by unmanaged endpoint drivers. The VM driver boots a cached bootstrap `rootfs.ext4`, prepares requested OCI images inside a bootstrap VM with `umoci`, attaches the prepared image disk read-only, and gives each sandbox a writable `overlay.ext4` for merged-root changes and runtime material. The driver persists each accepted launch request beside the overlay and restarts those VMs on driver startup without recreating the overlay. |
| Extension | Out-of-tree drivers operated alongside the gateway. | Whatever boundary the driver implements. | Selected by a custom `compute_drivers = ["<name>"]` entry with `[openshell.drivers.<name>].socket_path`, or at launch time by pairing `--drivers <name>` with `--compute-driver-socket=<path>`. A launch-time endpoint may use a canonical built-in name to preserve its driver-config key while replacing in-process construction. The gateway connects to an operator-provisioned UDS, snapshots `GetCapabilities`, and dispatches all sandbox lifecycle calls through `compute_driver.proto`. The driver process and socket lifecycle are operator-owned; the gateway does not spawn, supervise, or remove unmanaged extension drivers. The trust boundary is the socket's filesystem permissions: the operator must ensure only the gateway uid can read/write it. |
| VM | Experimental host-supervised microVM isolation. | Per-sandbox libkrun VM with a portable guest process leaf. | Managed endpoint-backed driver. The gateway spawns `openshell-driver-vm`; the driver starts a native logical supervisor and connects it to the guest over authenticated virtio-vsock. The guest has no gateway credentials. The existing custom kernel, cached bootstrap `rootfs.ext4`, in-VM `umoci` preparation, read-only image disk, writable `overlay.ext4`, and restart persistence remain unchanged. |
| Extension | Out-of-tree drivers operated alongside the gateway. | Whatever boundary the driver implements. | Selected by a non-reserved custom `compute_drivers = ["<name>"]` entry with `[openshell.drivers.<name>].socket_path`, or at launch time by pairing `--drivers <name>` with `--compute-driver-socket=<path>`. Reserved built-in names such as `vm`, `docker`, `podman`, and `kubernetes` cannot be used as unmanaged socket endpoints. The gateway connects to a UDS the operator already provisioned, runs `GetCapabilities`, logs the advertised `driver_name`, and dispatches all sandbox lifecycle calls through `compute_driver.proto`. The driver process and socket lifecycle are operator-owned; the gateway does not spawn, supervise, or remove unmanaged extension drivers. The trust boundary is the socket's filesystem permissions: the operator must ensure only the gateway uid can read/write it. |

Per-sandbox CPU and memory values currently enter the driver layer through
template resource limits. Docker and Podman apply them as runtime limits.
Expand Down Expand Up @@ -255,25 +266,29 @@ Runtime-specific implementation notes belong in the driver crate README:
- `crates/openshell-driver-kubernetes/README.md`
- `crates/openshell-driver-vm/README.md`

The combined VM topology runs `openshell-sandbox` as guest PID 1. libkrun
executes the driver-owned guest bootstrap as PID 1, and the bootstrap preserves
that identity when it execs the supervisor after mounting and network setup.
The VM topology is delegated. libkrun executes the driver-owned guest bootstrap
as PID 1; after mounting and network setup it execs `openshell-sandbox vm-guest`
as the portable process leaf. The native host supervisor drives RFC 0012 over a
driver-private, token-authenticated virtio-vsock transport. Lifecycle,
exec/PTY, loopback forwarding, and mediated egress all cross this transport;
gateway JWT and mTLS material remain on the host.

## Supervisor Delivery

The supervisor must be available inside each sandbox workload:
The logical supervisor or its boundary-local process leaf must be available at
the placement selected by the runtime:

| Runtime | Delivery model |
|---|---|
| Docker | Bind-mounted local supervisor binary, or a binary extracted from the configured supervisor image. |
| Podman | Read-only OCI image volume by default; host-cached bind mount when `userns` is configured. |
| Kubernetes | Supervisor image side-loaded into the sandbox pod by image volume or init container. |
| VM | Embedded in the guest rootfs bundle. |
| VM | Native host `openshell-sandbox` beside the driver; portable Linux process leaf embedded in the guest rootfs bundle. |
| Extension | Defined by the out-of-tree driver. |

Driver-controlled environment variables must override sandbox image or template
values for sandbox ID, sandbox name, gateway endpoint, relay socket path, TLS
paths, and command metadata.
values for sandbox ID, sandbox name, relay path, and command metadata. Gateway
endpoint and TLS values go only to the logical supervisor placement.

## Process Identity

Expand All @@ -285,7 +300,8 @@ driver then supplies one authoritative identity input to the supervisor:
resolves the workspace from OCI `Config.WorkingDir` during that inspection.
- Kubernetes passes its platform-resolved numeric UID/GID, including OpenShift
SCC-derived values.
- VM keeps its existing guest identity behavior.
- VM resolves the configured numeric UID/GID on the host and transfers the pair
to the guest leaf as authenticated launch state.

Explicit numeric workload identities may use any Linux UID/GID from `1`
through `u32::MAX - 1`. UID/GID `0` remains prohibited as root, and
Expand Down
Loading
Loading