boxkite

Guide

Kubernetes deployment

Docker-compose mode is for kicking the tires. This is the real thing: one isolated pod per session, a fresh network namespace per exec'd command, and RBAC scoped as tightly as Kubernetes actually allows.

1. Apply the manifests

Three files, all under deploy/ — edit the namespace and service-account references in each to match your cluster first.

terminal
kubectl apply -f deploy/rbac.yaml
kubectl apply -f deploy/network-policy.yaml
kubectl apply -f deploy/pod-security-policy.yaml

rbac.yamlgrants the control-plane's ServiceAccount only what it needs to create/delete/patch pods and per-pod sidecar-auth Secrets — its own comments disclose that Kubernetes RBAC can't scope those verbs to sandbox-labeled resources only, which is exactly why the next two files exist as defense in depth.

network-policy.yaml is a real starting manifest, not an illustrative snippet — but the object-storage egress destination is a placeholder you must fill in for your actual backend. Read its header comment fully before applying; it documents CNI-dependent enforcement gaps, including a specific verification command for the cloud metadata endpoint (169.254.169.254).

pod-security-policy.yaml is a ValidatingAdmissionPolicy that blocks hostNetwork/hostPID/hostIPC/hostPath/privilegedfor any pod created in your namespace — a cluster-level backstop in case the control-plane's RBAC credential is ever compromised. Apply it even if you also follow the dedicated-namespace convention rbac.yamlrecommends, since Kubernetes doesn't enforce that convention on its own.

Prefer Helm? deploy/helm/boxkite/ wraps these same three manifests (plus the image-builder RBAC/NetworkPolicy) into one chart with values.yaml exposing the same knobs:

terminal
helm install boxkite deploy/helm/boxkite --namespace your-namespace

2. Build and push the images

Two images live under deploy/: sandbox.Dockerfile (the code-execution container — non-root, all capabilities dropped, read-only root filesystem) and sidecar.Dockerfile (the HTTP API + storage-sync container).

terminal
docker build -f deploy/sandbox.Dockerfile -t your-registry/boxkite-sandbox:latest .
docker build -f deploy/sidecar.Dockerfile -t your-registry/boxkite-sidecar:latest .
docker push your-registry/boxkite-sandbox:latest
docker push your-registry/boxkite-sidecar:latest

3. Point the control-plane at your images

.env
SANDBOX_IMAGE=your-registry/boxkite-sandbox:latest
SIDECAR_IMAGE=your-registry/boxkite-sidecar:latest
SANDBOX_NAMESPACE=your-namespace

SandboxManager/WarmPoolManager build pod specs programmatically from these — deploy/pod-template.yamlis a checked-in reference manifest of that exact spec (kept in sync by a parity test) for self-hosters who'd rather review a static YAML file than the Python that generates it.

4. Cluster prerequisite: node-level PID limit

CPU/memory requests and limits are enforced on every sandbox pod, but the Kubernetes Pod API has no resources.limits.pids-equivalent field — a fork bomb run via /execcan exhaust a node's PID table before CPU/memory limits would otherwise throttle it, a cross-tenant DoS lever against other pods on the same node. Set --pod-max-pids(or your CNI/runtime's equivalent) at the kubelet level — this can't be expressed in a pod spec at all.

Apple Silicon / arm64

The sandbox image's build deliberately fails on arm64 — its pinned Chrome-for-Testing build has no Linux arm64 release, and silently falling back to an older bundled Chromium would reintroduce the vulnerability the pin exists to close. Build on an amd64 CI runner or cloud host.