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.
kubectl apply -f deploy/rbac.yaml
kubectl apply -f deploy/network-policy.yaml
kubectl apply -f deploy/pod-security-policy.yamlrbac.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:
helm install boxkite deploy/helm/boxkite --namespace your-namespace2. 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).
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:latest3. Point the control-plane at your images
SANDBOX_IMAGE=your-registry/boxkite-sandbox:latest
SIDECAR_IMAGE=your-registry/boxkite-sidecar:latest
SANDBOX_NAMESPACE=your-namespaceSandboxManager/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