# CubePod Full Documentation Corpus (v0.4.0) Source: https://cubepod.cubeplex.ai --- # E2B Drop-in Compatibility URL: https://cubepod.cubeplex.ai/getting-started/e2b-compatibility/ CubePod provides **wire-level drop-in compatibility** with official E2B SDKs (`e2b`, `e2b-code-interpreter`, `@e2b/code-interpreter`) as specified in [CEP-0018](/ceps/0018-e2b-protocol-compatibility/) and [CEP-0019](/ceps/0019-e2b-template-api/). Existing LangChain, LlamaIndex, AutoGen, CrewAI, OpenHands, and Smolagents pipelines can switch from hosted E2B to self-hosted persistent Kata MicroVMs simply by setting environment variables. ## Pointing Official E2B SDKs at CubePod Configure your environment according to whether `cubepod-gateway` sits behind HTTPS wildcard ingress or is accessed over local HTTP: ### Production HTTPS + Wildcard Ingress ```bash export E2B_DOMAIN="cubepod.example.com" export E2B_API_KEY="cpod_live_secret" ``` When `E2B_DOMAIN` is set, official E2B SDKs send control-plane calls to `api.` and sandbox port traffic to `-.` (`cubepod-gateway` matches `^([0-9]+)-([a-zA-Z0-9_-]+)\.(.*)$` in the `Host` header). ### Local HTTP / Direct Gateway When testing against `http://127.0.0.1:8080` without TLS or wildcard DNS, set both `E2B_API_URL` and `E2B_SANDBOX_URL` (the same configuration used by CubePod's `test/e2b-compat/` suite): ```bash export E2B_API_URL="http://127.0.0.1:8080" export E2B_SANDBOX_URL="http://127.0.0.1:8080" export E2B_API_KEY="cpod_test_api_key" ``` ## Unmodified Official E2B Examples ### Official `e2b` Python SDK (Sync & Async) ```python from e2b import Sandbox # Uses E2B_API_URL / E2B_SANDBOX_URL / E2B_DOMAIN automatically with Sandbox.create("base", timeout=300) as sbx: sbx.files.write("/workspace/data.txt", "hello from e2b sdk") result = sbx.commands.run("cat /workspace/data.txt") print(result.stdout) ``` ### Official `e2b-code-interpreter` SDK `podd` exposes stateful execution contexts for 6 languages (`python`, `bash`, `javascript`, `typescript`, `r`, `java`) over NDJSON streams compatible with `e2b-code-interpreter`: ```python from e2b_code_interpreter import Sandbox with Sandbox.create() as sbx: sbx.run_code("x = 21 * 2") execution = sbx.run_code("print(f'result={x}')") print(execution.logs.stdout) ``` ## Protocol Surface Implemented by `cubepod-gateway` `cubepod-gateway` (`pkg/gateway/`) implements both E2B's control plane and data plane (including Connect-RPC `envd` semantics on port `49983`): | Surface | Endpoints Implemented | Behavior in CubePod | | :--- | :--- | :--- | | **Control Plane** | `POST /sandboxes`, `POST /v2/sandboxes`, `GET /sandboxes`, `GET /v2/sandboxes`, `GET /sandboxes/{id}`, `DELETE /sandboxes/{id}` | Provisions or lists `PodBox` sandboxes; always populates mandatory E2B fields (`sandboxID`, `templateID`, `clientID`, `envdVersion`, `state`, `startedAt`, `endAt`, `cpuCount`, `memoryMB`, `diskSizeMB`). | | **Lifecycle & Leases** | `POST /sandboxes/{id}/refreshes`, `POST /sandboxes/{id}/timeout`, `POST /sandboxes/{id}/pause`, `POST /sandboxes/{id}/connect` | Renews idle timers/leases, tears down compute on pause while retaining PVCs, and transparently auto-resumes paused sandboxes on `connect`. | | **Commands & Streaming** | `POST /sandboxes/{id}/commands`, `POST /sandboxes/{id}/commands/run`, `GET .../logs?cursor=N`, `POST .../kill`, Connect-RPC `/process.Process/{Start,SendSignal,Connect,List}` | Executes commands inside the guest as unprivileged UID/GID `1000:1000` (`cubeplex`) with cursor-based incremental streaming and PGID cleanup. | | **Filesystem & Sync** | `PUT/GET/DELETE /sandboxes/{id}/files`, `GET .../files/list`, `POST .../files/archive-sync`, Connect-RPC `/filesystem.Filesystem/{Read,Write,List,ListDir,Stat,Remove,MakeDir}` | Performs direct guest I/O and path-traversal-safe `.tar`/`.tar.gz` directory sync against persistent `/workspace` or RootFS paths. | | **Code Interpreter** | `POST /sandboxes/{id}/execute`, `POST /sandboxes/{id}/code/execute`, `/contexts` | Manages stateful multi-language REPL kernels inside `podd` (`python`, `bash`, `javascript`, `typescript`, `r`, `java`). | | **Subdomain Port Routing** | `Host: -.` and `GET/POST /sandboxes/{id}/endpoints/{port}` | Auto-resumes paused sandboxes if needed, rewrites `/envd/*` to `podd` (`:49983`), and proxies arbitrary guest app ports or mints signed `/panel/{token}/` URLs. | | **Template Catalog & Builds** | `/templates`, `/v2/templates`, `/v3/templates`, `/templates/{id}/builds/{buildID}/status`, `/templates/{id}/files/{hash}` | Enabled via `--e2b-template-api=true` (or `-feature-gate E2BTemplateAPI=true`) for `e2b template build` workflows. | ## CubePod Superset Capabilities Beyond E2B When you use CubePod's native SDKs ([Python](/sdks/python/), [TypeScript](/sdks/typescript/), [Go & CLI](/sdks/go-and-cli/)) or REST superset endpoints, you unlock features not present in standard E2B: - **Persistent Writable OS RootFS & Non-Destructive OS Reset**: `POST /sandboxes/{id}/reset-rootfs` increments `spec.rootfsGeneration`, allocating a fresh OS `upperdir` PVC while preserving `/workspace`. - **1-Second CoW Forking**: `POST /sandboxes/{id}/fork` triggers `FIFREEZE` quiesce -> CSI/reflink clone -> `FITHAW` to branch both OS and `/workspace` state without disturbing the running parent. - **Zero-Trust `cpodref_` Egress Vault**: Pass `secretBindings` at sandbox creation so agents only ever see `cpodref_` placeholders while `cubepod-egress` injects real credentials at the network boundary. - **Lazy Interactive Desktops**: Call `/sandboxes/{id}/desktop/{start,status,stop}` to spin up `Xvfb`, Chromium CDP, `noVNC`, and `ttyd` on demand behind signed `/panel/{token}/` endpoints. ## Next Steps - See the **[API Reference](/api/)** for the full OpenAPI 3.1 contract (`api/openapi/cubepod-v1.yaml`). - Compare native clients in **[Python SDK](/sdks/python/)** and **[TypeScript SDK](/sdks/typescript/)**. - Inspect **[CRD Reference](/reference/crds/)** for Kubernetes `PodBox` and `PodTemplate` schemas. --- # Introduction URL: https://cubepod.cubeplex.ai/getting-started/introduction/ **CubePod** is an open-source, cloud-native **Persistent Writable RootFS MicroVM Execution Platform for AI Agents**, built to power **CubePlex** and the broader **E2B / MCP** open-source ecosystem. Unlike stateless container runners or ephemeral sandboxes that discard installed dependencies, browser state, and system configuration whenever a session pauses or restarts, CubePod gives every AI agent a hardware-isolated sandbox whose **entire OS filesystem (`/`, `/usr`, `/etc`, `/var/lib`) and user project directory (`/workspace`) both persist independently**. ## Why Persistent Writable RootFS Matters Modern autonomous agents do far more than evaluate isolated Python snippets. Over multi-hour or multi-day tasks they install system packages (`apt`, `pip`, `npm`, Playwright browsers), configure systemd or background daemons, authenticate CLI tools, compile code, and edit repositories. Standard sandbox platforms leave you with an awkward trade-off: - **Ephemeral MicroVMs** offer strong hypervisor isolation, but their RootFS lives only in RAM/scratch layers — pausing or restarting wipes OS packages and system state unless you rebuild custom base images on every mutation. - **Persistent Volume Containers** keep `/workspace` alive, but lack hardware VM boundaries and force agents to reinstall OS packages or lose system config across pod restarts. CubePod removes this compromise by booting **stock, unmodified OCI images** inside **Kata Containers MicroVMs** backed by **dual persistent volumes**: one volume holds an `OverlayFS` writable `upperdir` for the entire Linux RootFS, while a second volume exclusively stores `/workspace`. ## Platform Comparison | Capability | Plain Containers | Ephemeral MicroVMs | CubePod | | :--- | :--- | :--- | :--- | | **Compute Isolation** | Shared host Linux kernel | Hardware VM (`Firecracker` / `QEMU`) | Hardware VM (`Kata QEMU` / `Cloud Hypervisor`) + local Docker loopback | | **OS RootFS Persistence** | Ephemeral writable layer | Ephemeral scratch layer | **Persistent `OverlayFS` `upperdir`** (`-rootfs-pvc`) | | **Workspace Persistence** | Optional PVC mount | Optional or lost on stop | **Decoupled persistent volume** (`-workspace-pvc`) | | **RootFS Reset / OS Upgrade** | Recreate container | Image rebuild | Monotonic `rootfsGeneration` swap — **`/workspace` stays intact** | | **Cold Start Latency** | ~1–3 s | ~150 ms | **`< 50 ms`** via pre-attached `PodWarmPool` CAS claims | | **Compute Pause-to-Zero** | Deletes state on stop | Snapshot or ephemeral stop | `desiredState: Paused` tears down compute (`0 CPU / 0 RAM`), keeps dual PVCs | | **State Cloning (CoW Fork)** | Slow image commit | VM snapshot | **Crash-consistent CoW fork** (`FIFREEZE` -> CSI clone / reflink -> `FITHAW`) | | **Secret & Egress Safety** | Real env vars inside sandbox | Real env vars inside sandbox | **`cpodref_` placeholders** + selective MITM over per-pod mTLS | | **SDK & Protocol Ecosystem** | Custom APIs | E2B REST / Connect-RPC | **100% E2B drop-in compatible** + native Python, TS, Go, CLI, MCP | ## Core Architectural Pillars 1. **[Dual-Volume Persistence](/concepts/dual-volume-persistence/)**: Isolates the writable OS RootFS (`-rootfs-pvc` or `-rootfs-gen-pvc`) from user project files (`-workspace-pvc` mounted at `/workspace`). Incrementing `rootfsGeneration` wipes or upgrades OS mutations without losing a byte in `/workspace`. 2. **[Clean OCI Images via `podd-init` & `podd`](/concepts/in-guest-init-and-podd/)**: Runtime binaries are never baked into user images. An `initContainer` (`podd-installer`) or host bind-mount injects `/opt/cubepod/bin/{podd-init,podd}` at startup. A single-threaded Rust PID 1 (`podd-init`) checks/heals `ext4`, mounts `OverlayFS`, pivots root (`pivot_root`), and execs into `podd` (`:49983`). 3. **[WarmPool, Pause-to-Zero, CoW Fork & Stateless HA](/concepts/lifecycle-warmpool-fork/)**: `PodWarmPool` pre-attaches standby MicroVMs claimed atomically in `< 50 ms`. `spec.forkFrom` performs crash-consistent cloning (`FIFREEZE`/`FITHAW` + CSI or `cp --reflink=auto`), and `cubepod-gateway` reconstructs state directly from Kubernetes `PodBox` CRDs for multi-replica HA. 4. **[Drop-in E2B Compatibility](/getting-started/e2b-compatibility/)**: Point `E2B_DOMAIN` or `E2B_API_URL` at `cubepod-gateway` so existing `e2b`, `e2b-code-interpreter`, `@e2b/code-interpreter`, LangChain, LlamaIndex, and OpenHands workflows run unmodified. 5. **[Zero-Trust Egress & Secret Vault](/concepts/zero-trust-egress/)**: Real API keys never enter sandbox memory or disk. Only `cpodref_` 32-character Base32 placeholders are injected; `cubepod-egress` intercepts outbound traffic (`nftables` + DNS proxy + selective HTTPS MITM) and swaps placeholders over per-pod mTLS against `cubepod-gateway`. 6. **[Lazy Interactive Desktop & Signed Panel Proxy](/concepts/desktop-and-panel-proxy/)**: GUI images start with ~0 MiB desktop RAM overhead and launch `Xvfb`, `openbox`, Chromium CDP (`:9222`), `x11vnc`/`noVNC` (`:8080`), and `ttyd` (`:7681`) on demand, exposed securely via HMAC-SHA256 `/panel/{token}/` URLs. ## Dual-Runtime Fidelity Every CubePod subsystem runs with behavioral parity across two environments: - **Production Kubernetes (`Kata + CSI Block`)**: Hardware-isolated Pods (`kata-qemu` / `kata-clh`) attaching two `volumeMode: Block` PVCs (`/dev/cubepod-rootfs` and `/dev/cubepod-workspace`), with `volumeMode: Filesystem` fallback when CSI raw block storage is unavailable. - **Local Development (`Docker + Loopback ext4`)**: Running `CUBEPOD_RUNTIME_MODE=docker-loopback` (including via Docker Compose) backs each sandbox with sparse `.ext4` loopback files and runs the exact same `podd-init` + `podd` in-guest stack on your laptop. ## Next Steps - Launch a local stack and run your first persistent sandbox in the **[Quickstart](/getting-started/quickstart/)**. - Connect existing E2B agents with **[E2B Drop-in Compatibility](/getting-started/e2b-compatibility/)**. - Dive into the control and data plane in **[Platform Architecture](/concepts/architecture/)**. - Deploy to production clusters with **[Kubernetes & Helm Deployment](/deployment/kubernetes-helm/)**. --- # Quickstart URL: https://cubepod.cubeplex.ai/getting-started/quickstart/ Get CubePod running locally in minutes using `docker-compose`, then interact with your persistent sandboxes through the Python SDK, TypeScript SDK, or `cubepod` CLI. ## Prerequisites - **Docker Engine** with Docker Compose v2 (`docker compose`) - **Python 3.11+** (for the Python SDK) and/or **Node.js 20+** (for the TypeScript SDK) - *(Optional, if building binaries from source)* **Go 1.22+** and **Rust** (`cargo`) ## 1. Start the Local Control Plane From the root of the CubePod repository, boot `cubepod-gateway` (`:8080`), `cubepod-controller` (`:8081`), and `cubepod-egress` (`:18080`): ```bash docker compose -f deploy/docker-compose/docker-compose.yaml up -d --build ``` Verify that the gateway is healthy: ```bash curl -fsS http://127.0.0.1:8080/health ``` > **Note:** `deploy/docker-compose/docker-compose.yaml` sets `CUBEPOD_RUNTIME_MODE=docker-loopback` on `cubepod-gateway`, mounts `/var/run/docker.sock` and `cubepod-volumes`, and starts `cubepod-egress` with `-default-allow=true` for local development. For building all Rust and Go binaries locally without Docker Compose, run `make build` (`bin/podd-init`, `bin/podd`, `bin/cubepod-controller`, `bin/cubepod-gateway`, `bin/cubepod-egress`, and `bin/cubepod`). ## 2. Create a Sandbox & Verify Persistence Choose your preferred client SDK or the native CLI below. ### Python SDK Install the Python SDK from PyPI or from `sdks/python/` in the repository: ```bash pip install cubepod ``` Create a sandbox, write files to `/workspace`, execute commands, pause compute (`0 CPU / 0 RAM`), reconnect, and create a Copy-on-Write fork: ```python from cubepod import CubePod with CubePod(base_url="http://127.0.0.1:8080") as client: # 1. Create a persistent sandbox sbx = client.create("cubeplex-ubuntu24-desktop", timeout_seconds=3600) print("Created:", sbx.sandbox_id, "state:", sbx.state) # 2. Write a file in /workspace and run a command client.write_file(sbx.sandbox_id, "/workspace/hello.txt", b"persistent data\n") res = client.execute(sbx.sandbox_id, "cat /workspace/hello.txt && id") print("Output:", res.output) # 3. Pause compute (retains both rootfs and /workspace volumes) client.pause(sbx.sandbox_id) # 4. Resume on connect and verify state survived pause client.connect(sbx.sandbox_id) print("After resume:", client.read_text_file(sbx.sandbox_id, "/workspace/hello.txt")) # 5. Create an independent Copy-on-Write fork child = client.fork(sbx.sandbox_id) print("Forked child sandbox:", child.sandbox_id) # 6. Clean up both sandboxes client.delete(child.sandbox_id) client.delete(sbx.sandbox_id) ``` ### TypeScript / Node.js SDK The official TypeScript SDK has zero external runtime dependencies on Node.js 20+: ```bash npm install cubepod ``` ```typescript import { Sandbox } from 'cubepod'; const sandbox = await Sandbox.create('cubeplex-ubuntu24-desktop', { baseUrl: 'http://127.0.0.1:8080', timeoutSeconds: 3600, }); await sandbox.files.write('/workspace/hello.txt', 'persistent data\n'); const cmd = await sandbox.commands.run('cat /workspace/hello.txt'); console.log('Command output:', cmd.output); // Run code via podd's built-in stateful interpreter (Python, Bash, JS, TS, R, Java) const exec = await sandbox.code.runCode('print(6 * 7)', { language: 'python' }); console.log('Interpreter:', exec.output); // Pause and later reconnect transparently await sandbox.pause(); await sandbox.connect(); // Crash-consistent CoW fork const child = await sandbox.fork(); await child.kill(); await sandbox.kill(); ``` ### `cubepod` CLI Build the CLI with `make build-cli` (outputs `bin/cubepod`), then drive the full lifecycle from your terminal: ```bash export CUBEPOD_GATEWAY_URL="http://127.0.0.1:8080" # Check gateway health ./bin/cubepod health # Create a sandbox and run a command ./bin/cubepod sandbox create --template cubeplex-ubuntu24-desktop ./bin/cubepod sandbox exec "uname -a && id" # Upload/read files and sync a directory as tar.gz ./bin/cubepod sandbox write-file /workspace/app.py "print('hello from cubepod')" ./bin/cubepod sandbox read-file /workspace/app.py ./bin/cubepod sandbox archive-sync /workspace/project ./local-project # Pause, reconnect, fork, reset OS rootfs (keeping /workspace), or delete ./bin/cubepod sandbox pause ./bin/cubepod sandbox connect ./bin/cubepod sandbox fork ./bin/cubepod sandbox reset-rootfs ./bin/cubepod sandbox delete ``` ## 3. Reset RootFS Without Losing `/workspace` If an agent installs broken system libraries or corrupts OS state under `/etc` or `/usr`, you can reset the RootFS layer while keeping `/workspace` intact: ```python # Wipes the persistent OverlayFS upperdir (allocates rootfs-gen) # while re-attaching the exact same /workspace volume. client.reset_rootfs(sbx.sandbox_id) ``` Read [Dual-Volume Persistence](/concepts/dual-volume-persistence/) to see how `rootfsGeneration` keeps OS upgrades and project data strictly separated. ## 4. Launch an Interactive Desktop (Optional) When using a desktop-capable template image (see [Interactive Desktop & Panel Proxy](/concepts/desktop-and-panel-proxy/)), you can lazily start `Xvfb`, `openbox`, Chromium CDP, `noVNC`, and `ttyd` inside the sandbox: ```python session = client.start_desktop( sbx.sandbox_id, resolution="1280x800x24", initial_url="https://example.com", ) print("noVNC URL:", session.desktop_url) print("Terminal URL:", session.terminal_url) print("Chromium CDP URL:", session.cdp_url) ``` > **Tip:** If your sandbox template does not contain `/opt/cubepod/desktop/cubepod-desktop-ctl`, `start_desktop` fails fast with HTTP `422 desktop_not_installed`. ## Next Steps - Run existing `e2b` / `e2b-code-interpreter` code unchanged via **[E2B Drop-in Compatibility](/getting-started/e2b-compatibility/)**. - Explore the internals in **[Platform Architecture](/concepts/architecture/)**. - Deploy CubePod on Kubernetes with Kata Containers and CSI raw block storage in **[Kubernetes & Helm Deployment](/deployment/kubernetes-helm/)**. --- # Platform Architecture URL: https://cubepod.cubeplex.ai/concepts/architecture/ CubePod separates **agent orchestration**, **persistent storage**, **hardware compute isolation**, **in-guest execution**, and **outbound credential security** into focused, loosely coupled components. Every component maintains behavioral parity between **Production Kubernetes (`Kata + CSI Block`)** and **Local Development (`Docker + Loopback ext4`)**. ## End-to-End Topology ```mermaid flowchart TB SDK["Agent / SDK Client (cubepod, e2b, MCP)"] -->|"REST, Connect-RPC, SSE, Panel HTTP/WS"| GW["cubepod-gateway (:8080, Stateless HA)"] GW -->|"CRD Watch / CRUD / Status"| K8S["Kubernetes API Server (cubepod.io/v1alpha1)"] GW -->|"Reconcile / Pause / Fork API"| CTRL["cubepod-controller (:8081)"] CTRL -->|"Provision / Claim / Snapshot"| K8S GW -->|"PodIP:49983 & Panel Ports"| PODD["podd Execution Daemon (:49983)"] subgraph POD["Sandbox Pod (Kata MicroVM or Docker Container)"] INIT["initContainer: podd-installer & egress-init"] -->|"Inject /opt/cubepod/bin + nftables"| PID1["PID 1: podd-init (Single-Threaded Rust)"] PID1 -->|"pivot_root + execvp"| PODD ROOTFS["OverlayFS RootFS (upperdir on rootfs-pvc)"] --- PID1 WS["/workspace (workspace-pvc, 1000:1000)"] --- PID1 PODD -->|"Guest Outbound Traffic"| NFT["nftables cubepod_egress"] NFT -->|"Redirect TCP 80/443 & DNS 53"| EGRESS["cubepod-egress Sidecar (:18080, :18053, :18081, :18443)"] end EGRESS -->|"mTLS POST /internal/v1/egress/exchange"| GW EGRESS -->|"Passthrough Splice or Real Auth Header"| EXT["External APIs (GitHub, OpenAI, Anthropic, Web)"] ``` ## Control Plane & Operator Components ### 1. Stateless HA Control Plane Gateway (`cubepod-gateway`) Implemented in `cmd/cubepod-gateway/` and `pkg/gateway/`, `cubepod-gateway` listens on `:8080` and terminates all client traffic: - **E2B-Compatible & Superset API**: Serves sandbox lifecycle (`POST /sandboxes`, `/v2/sandboxes`), command/filesystem data plane, Connect-RPC (`/process.Process/*`, `/filesystem.Filesystem/*`), wildcard subdomain routing (`-.`), and optional template builds (`--e2b-template-api`). - **Auto-Resume & Readiness Polling**: When a request arrives for a `Paused` sandbox, the gateway drives `desiredState: Running` and polls `podd` `/health` via `waitForPoddReady` before forwarding traffic. - **Stateless Multi-Replica HA**: All authoritative sandbox metadata is persisted on Kubernetes `PodBox` annotations (`cubepod.io/template-id`, `cubepod.io/timeout-seconds`, `cubepod.io/volume-key`, `cubepod.io/metadata-*`, `cubepod.io/resolved-image`, `cubepod.io/forked-from`) and `status.podIP`. Any replica suffering an in-memory cache miss reconstructs state via `resolveSandbox` directly from the cluster. - **Signed Panel Proxy & Secret Vault**: Mints and validates HMAC-SHA256 `/panel/{token}/` URLs for interactive desktops and dev previews, and serves `POST /internal/v1/egress/exchange` over mTLS for `cpodref_` placeholder swapping. ### 2. Kubernetes Operator (`cubepod-controller`) Implemented in `cmd/cubepod-controller/` and `pkg/controller/`, `cubepod-controller` reconciles three custom resources defined in `deploy/helm/cubepod/crds/` under **`cubepod.io/v1alpha1`** (`api/v1alpha1/types.go` defines both `CRDGroup = "cubepod.io"` and `GroupName = "pod.cubeplex.ai"`): - **`PodBox` (`pb`, `pbox`)**: Represents a single persistent sandbox with independent RootFS and `/workspace` PVCs, lifecycle timeouts, `forkFrom` lineage, and `networkPolicy`. - **`PodTemplate` (`pt`, `ptemplate`)**: Defines base OCI image, `runtimeClassName` (`kata-qemu`, `kata-clh`, or empty for local dev), CPU/memory limits, storage class, and default egress posture. - **`PodWarmPool` (`pwp`, `warmpool`)**: Maintains pre-booted, pre-attached standby MicroVMs claimed via atomic Kubernetes CAS label updates in `< 50 ms`. The controller manages dual PVC provisioning, `rootfsGeneration` resets, CoW `ForkFrom` resolution across rootfs generations, idle/TTL reaping (`ReapIdle`), and per-pod ECDSA P-256 mTLS & MITM CA secrets (`ensureEgressSecretsLocked`). ## Data Plane & In-Guest Components ### 3. Clean Image Injection & In-Guest Rust Runtime (`podd-init` & `podd`) CubePod never requires baking proprietary agents or init scripts into user Dockerfiles: - `images/podd/Dockerfile` packages static Rust binaries into a minimal `busybox:1.37-musl` injector image. - An `initContainer` (`podd-installer`) copies `/opt/cubepod/bin/{podd-init,podd}` into a shared `emptyDir` volume (or Docker bind-mounts the binary directory read-only). - **`podd-init`** (`crates/podd-init/`) runs as a **single-threaded PID 1**: formats or heals `ext4` (`blkid` / `mkfs.ext4` / `e2fsck -p`), mounts the RootFS `OverlayFS` (`lowerdir=/`), attaches `/workspace` as UID/GID `1000:1000`, bind-mounts `/opt/cubepod` across the mount namespace, executes `pivot_root`, and `execvp`s into `podd`. - **`podd`** (`crates/podd/`) listens on `:49983` for command execution (cursor-tailed stdout/stderr, PGID kill, Connect-RPC), file transfer & streaming `tar.gz` `archive-sync`, crash-consistent `FIFREEZE`/`FITHAW` quiescing, and 6 persistent code-interpreter kernels. ### 4. Zero-Trust Egress Sidecar (`cubepod-egress`) Co-located inside the sandbox Pod as a dedicated UID `65532` sidecar container (`cmd/cubepod-egress/`, `pkg/egress/`), `cubepod-egress` enforces domain policies (`DenyHosts` > `AllowHosts` > `DefaultAction`), refuses unauthorized DNS (`RCODE=5` on `:18053`), receives `nftables`-redirected HTTP/TLS traffic (`:18081` / `:18443`), performs zero-copy splicing for normal sites, and selectively MITMs only secret-bound hosts to swap `cpodref_` placeholders over mTLS. ### 5. Lazy Interactive Desktop (`images/desktop/`) Optional desktop images launch cleanly with `sleep infinity` (~0 MiB idle RAM) and expose `/opt/cubepod/desktop/cubepod-desktop-ctl` to bring up `Xvfb`, `openbox`, Chromium CDP (`127.0.0.1:9222`), `x11vnc`/`websockify`/`noVNC` (`:8080`), and `ttyd` (`:7681`) on demand. ## Dual-Runtime Architecture Parity | Layer | Production Kubernetes (`Kata + CSI Block`) | Local Development (`Docker + Loopback`) | | :--- | :--- | :--- | | **Compute Isolation** | `Kata Containers` (`runtimeClassName: kata-qemu` / `kata-clh`) | Docker container (`CUBEPOD_RUNTIME_MODE=docker-loopback`) | | **Storage Backing** | Two `volumeMode: Block` PVCs (`/dev/cubepod-rootfs`, `/dev/cubepod-workspace`) or `volumeMode: Filesystem` fallback | Sparse `.ext4` loopback files per sandbox (`cubepod-volumes`) | | **In-Guest Mounts** | `podd-init` mounts `OverlayFS` (`lowerdir=/`) + `/workspace` inside VM kernel | Identical `podd-init` `e2fsck` + `OverlayFS` + `pivot_root` path | | **CoW Fork Mechanism** | `podd` `FIFREEZE` -> CSI `DataSourcePVC` / snapshot clone -> `FITHAW` | `podd` `FIFREEZE` -> `cp --reflink=auto --sparse=always -a` -> `FITHAW` | | **Runtime Binary Injection** | `podd-installer` `initContainer` -> `emptyDir` `/opt/cubepod/bin` | Read-only host bind-mount `-v :/opt/cubepod/bin:ro` | > **Note on Defaults:** Production Helm defaults (`deploy/helm/cubepod/values.yaml`) and `controller.NewReconciler()` target `volumeMode: Block`, `runtimeClassName: kata-qemu`, and `storageClassName: longhorn-v2-block-thin`. When running standalone `cmd/cubepod-controller` or `cmd/cubepod-gateway` binaries outside Helm, CLI flags default to `-volume-mode=Filesystem`, `-storage-class=nfs-storage`, and `-runtime-class=""` unless explicitly overridden. ## Next Steps - Understand volume separation in **[Dual-Volume Persistence](/concepts/dual-volume-persistence/)**. - Inspect the Rust PID 1 boot sequence in **[In-Guest Runtime (podd-init & podd)](/concepts/in-guest-init-and-podd/)**. - Explore `< 50ms` scheduling and cloning in **[Lifecycle, WarmPool, CoW Fork & HA](/concepts/lifecycle-warmpool-fork/)**. --- # Interactive Desktop & Signed Panel Proxy URL: https://cubepod.cubeplex.ai/concepts/desktop-and-panel-proxy/ Computer-use and browser-automation agents frequently need a real X11 display, Chromium DevTools Protocol (CDP), human-in-the-loop VNC viewing, and a live browser shell — without forcing non-GUI sandboxes to pay idle RAM overhead or punch per-sandbox firewall holes in Kubernetes. CubePod solves this with two tightly integrated mechanisms ([CEP-0004](/ceps/0004-gateway-and-panel-proxy/), [CEP-0021](/ceps/0021-interactive-desktop-and-panel-stack/)): a **lazy on-demand desktop stack (`images/desktop/`)** and a **stateless HMAC-SHA256 `/panel/{token}/` reverse proxy (`pkg/gateway/panel.go`)**. ## 1. Zero-Idle-Cost Lazy Desktop Stack Containers built from CubePod's desktop image (`images/desktop/`) boot into `CMD ["sleep", "infinity"]` with **~0 MiB desktop daemon memory overhead**. Graphical daemons remain dormant until a client explicitly starts a desktop session. All desktop processes are managed by `/opt/cubepod/desktop/cubepod-desktop-ctl` (`start`, `status`, `stop`), stored separately from `/opt/cubepod/bin` so runtime binaries remain decoupled: | Daemon | Bind / Display | Role & Configuration | | :--- | :--- | :--- | | **`Xvfb`** | Display `:99` | Virtual X11 framebuffer defaulting to `1280x800x24` (customizable via validated `WxHx{16\|24\|32}` resolution). | | **`openbox`** | Display `:99` | Lightweight X11 window manager for browser and desktop windows. | | **`chromium-wrapper`** | `127.0.0.1:9222` | Headless/headed Chromium exposing Chrome DevTools Protocol (CDP), optionally navigating to `initialUrl`. Cleans stale `SingletonLock` / `SingletonSocket` / `SingletonCookie` files from persisted RootFS across sandbox pauses or restarts. | | **`x11vnc`** | `127.0.0.1:5900` | Binds the `:99` X display locally over VNC. | | **`websockify` + `noVNC`** | `0.0.0.0:8080` | Bridges VNC WebSockets into browser-viewable `vnc.html`. | | **`ttyd`** | `:7681` | Streams an interactive web terminal (`/bin/bash`) starting in `/workspace`. | ### Reliability & Privilege Safety (`cubepod-desktop-ctl`) - **Privilege Separation**: Although `podd` spawns control hooks cleanly, `cubepod-desktop-ctl` drops privileges to UID/GID `1000:1000` (`cubeplex`) so browser caches, downloads, and shell sessions run unprivileged and share `/workspace` ownership automatically. - **Concurrency Locking**: Operations are serialized via `flock` so concurrent `start` / `stop` calls never spawn duplicate `Xvfb` or `x11vnc` instances. - **RootFS-Safe Chromium Recovery**: Because CubePod persists RootFS across pod restarts, Chromium's previous PID profile locks (`Singleton*`) would normally prevent relaunching after a `Paused` -> `Running` transition. `cubepod-desktop-ctl` cleans stale lock files before starting `chromium-wrapper`. > **Protocol Note:** The shipping `images/desktop/` stack serves **`noVNC` (`:8080`)**, **`ttyd` (`:7681`)**, and **Chromium CDP (`:9222`)**, while CubePod's panel reverse proxy and architecture blueprint also support proxying WebRTC/Neko streaming endpoints when supplied by custom images. ## 2. Gateway Desktop API (`pkg/gateway/desktop.go`) `cubepod-gateway` exposes three lifecycle endpoints (also available under `/v1/sandboxes/{id}/desktop/...`): - `POST /sandboxes/{id}/desktop/start` — validates resolution (`WxHx{16|24|32}`), auto-resumes the sandbox if `Paused`, verifies `/opt/cubepod/desktop/cubepod-desktop-ctl` exists (returning **`HTTP 422 desktop_not_installed`** on non-desktop images), starts the stack via `podd`, and returns ready URLs (`desktopUrl`, `terminalUrl`, `cdpUrl`). - `GET /sandboxes/{id}/desktop/status` — returns live status of `Xvfb`, `openbox`, `chromium`, `x11vnc`/`noVNC`, and `ttyd`. - `POST /sandboxes/{id}/desktop/stop` — gracefully tears down GUI daemons to reclaim RAM while preserving sandbox compute and files. Example response URLs returned by `start`: - `desktopUrl`: `/panel//vnc.html?autoconnect=true&resize=scale` - `terminalUrl`: interactive `ttyd` terminal endpoint (port `7681`) - `cdpUrl`: Chrome DevTools Protocol (CDP) endpoint for headless/headed Chromium automation (port `9222`) ## 3. Stateless Signed Panel Proxy (`pkg/gateway/panel.go`) Instead of provisioning per-sandbox Kubernetes `Ingress` objects or exposing container ports directly, `cubepod-gateway` issues short-lived, cryptographically signed panel URLs for **any** port inside a sandbox (desktop `8080`, `ttyd` `7681`, Jupyter, Vite/React dev servers, MCP UIs): 1. **Token Minting (`POST /sandboxes/{id}/endpoints/{port}`)**: Encodes payload `sandboxID:port:expUnix` (default TTL **`3600` seconds**), signs it with **HMAC-SHA256**, and outputs: ```text base64url(sandboxID:port:expUnix).base64url(hmacSha256) ``` 2. **Stateless Verification Across Replicas**: Any `cubepod-gateway` replica receiving `GET /panel/{token}/{rest...}` (or a WebSocket upgrade handshake) verifies the HMAC signature and expiration timestamp without needing central session storage. 3. **Auto-Resume & Live Reverse Proxy**: Using `resolveSandbox`, the gateway wakes the sandbox if it is `Paused`, resolves its current `:`, and reverse-proxies HTTP assets and bidirectional WebSocket frames (`noVNC`, `ttyd`, HMR dev servers) directly to the MicroVM. ## Next Steps - Try launching a desktop session in Python from the **[Quickstart](/getting-started/quickstart/#4-launch-an-interactive-desktop-optional)**. - Inspect the full gateway endpoints in the **[API Reference](/api/)**. - Read the design specifications in **[CEP-0004](/ceps/0004-gateway-and-panel-proxy/)** and **[CEP-0021](/ceps/0021-interactive-desktop-and-panel-stack/)**. --- # Dual-Volume Persistence URL: https://cubepod.cubeplex.ai/concepts/dual-volume-persistence/ A foundational invariant of CubePod is **Dual-Volume Independence**: the lifecycle of user project files in `/workspace` (`workspace-pvc`) is strictly decoupled from the container's persistent OS `OverlayFS` layer (`rootfs-pvc`). ## Why Two Independent Volumes? If an agent sandbox keeps everything on one volume or leaves the OS RootFS ephemeral: - **With an ephemeral RootFS**, any `apt-get install`, `npm install -g`, Python toolchain, Playwright browser binary, or `/etc` configuration disappears whenever compute scales to zero (`Paused`) or moves across Kubernetes nodes. - **With a single merged volume**, resetting a corrupted OS installation or upgrading the underlying base OCI template image forces a destructive wipe of the agent's source code, checkpoints, and datasets. CubePod avoids both failure modes by attaching **two independent persistent volumes** to every `PodBox`. ```mermaid flowchart LR IMG["Base OCI Image (lowerdir=/, read-only)"] --> OVL["Linux OverlayFS (merged /)"] RPVC["rootfs-pvc / rootfs-genN-pvc (ext4, upper/ + work/)"] -->|"upperdir + workdir"| OVL OVL -->|"pivot_root"| GUEST["Guest Root Filesystem (/, /usr, /etc, /home, /var)"] WPVC["workspace-pvc (ext4, UID/GID 1000:1000)"] -->|"Independent Mount"| WDIR["/workspace (User Code, Datasets, Artifacts)"] ``` ## Volume Layout Inside the Sandbox | Volume | Default Size (`manifests.go`) | PVC Naming Convention | Mount Path in Guest | Ownership & Purpose | | :--- | :--- | :--- | :--- | :--- | | **RootFS Upper** | `10Gi` | `-rootfs-pvc` (gen `1`) or `-rootfs-gen-pvc` (gen `> 1`) | Block device `/dev/cubepod-rootfs` -> mounted at `/mnt/cubepod-upper` -> `OverlayFS` `upperdir` + `workdir` | Persists every OS-level mutation (`/usr`, `/etc`, `/var`, installed packages, system libs) on top of the read-only OCI `lowerdir`. | | **Workspace** | `20Gi` | `-workspace-pvc` | Block device `/dev/cubepod-workspace` -> mounted directly at `/workspace` | Dedicated workspace storage initialized to `UID:GID 1000:1000` (`cubeplex`). Survives base image upgrades and `RootFS` resets untouched. | In Kubernetes, both PVCs carry deterministic labels (`pod.cubeplex.ai/box: ` and `pod.cubeplex.ai/vol-type: rootfs | workspace`) so garbage collection, snapshot cloning, and multi-generation tracking remain unambiguous. ## Non-Destructive OS Reset (`rootfsGeneration`) When an AI agent breaks system libraries, pollutes `/etc`, or needs a newer base OCI image, CubePod does not discard `/workspace`: 1. A client calls `POST /sandboxes/{id}/reset-rootfs` (or `client.reset_rootfs(id)` / `sandbox.resetRootfs()`), or updates `PodBox.spec.rootfsGeneration`. 2. `cubepod-controller` transitions the `PodBox` phase to `ResettingRootFS` (`Reconcile` compares `spec.rootfsGeneration` against `status.observedRootfsGeneration`). 3. The controller tears down the running Pod, allocates a **fresh RootFS PVC** named `-rootfs-gen-pvc`, and leaves the original `-workspace-pvc` (`status.workspacePVCName`) completely intact. 4. A new Pod boots against the new `upperdir` while re-attaching the exact same `/workspace` volume, updating `status.observedRootfsGeneration` and `status.rootfsPVCName`. > **Important for CoW Fork:** When a forked sandbox (`spec.forkFrom`) is later created from a parent that has reset its RootFS one or more times, the controller's `resolveParentPVCNames` reads `status.RootfsPVCName` (e.g., `-rootfs-gen3-pvc`) instead of assuming `-rootfs-pvc`, ensuring forks always clone the parent's active RootFS generation. ## Three Storage Backends Across Production and Local Dev CubePod executes the same dual-volume abstraction across three storage modes implemented in `pkg/controller/manifests.go` and `pkg/runtime/docker.go`: ### 1. CSI Raw Block (`volumeMode: Block` + Kata MicroVM) In production (`kata-qemu` / `kata-clh` with Longhorn V2 SPDK, OpenEBS LVM-LocalPV, Ceph RBD, or cloud EBS): - `BuildBlockPVCs` requests two `volumeMode: Block` PVCs and injects them into the Pod's `volumeDevices` at `/dev/cubepod-rootfs` and `/dev/cubepod-workspace`. - Inside the Kata guest VM kernel, `podd-init` checks `blkid`, formats fresh volumes with `mkfs.ext4 -F -L ... -m 0 -E lazy_itable_init=0,lazy_journal_init=0`, or heals existing volumes with `e2fsck -p`, then mounts `ext4` (`-o noatime`) and builds the `OverlayFS` inside the MicroVM's own Linux kernel. - **Why Raw Block Inside Kata Matters**: Mounting `OverlayFS` directly on a containerd overlayfs root inside a standard host container fails in Linux kernels that forbid nested `OverlayFS`-on-`OverlayFS` (`overlay` `lowerdir` cannot be another `overlay`). Inside a Kata MicroVM (`virtio-blk` + guest kernel), `podd-init` runs cleanly without polluting host `containerd` image layers. ### 2. CSI Filesystem Fallback (`volumeMode: Filesystem`) When deploying into standard Kubernetes clusters without raw block CSI support (`runtimeClassName: ""`, `-volume-mode=Filesystem`): - `BuildBlockPVCs` provisions normal `volumeMode: Filesystem` PVCs mounted into `/mnt/cubepod/rootfs` and `/workspace`. - `podd-init` detects the absence of raw block devices (or `CUBEPOD_SKIP_PIVOT=1`), records `pivoted: false` in `/run/cubepod/init-state.json`, and hands off cleanly to `podd` so `/workspace` still persists across Pod restarts. ### 3. Local Docker Loopback (`CUBEPOD_RUNTIME_MODE=docker-loopback`) For laptop development without Kubernetes: - `pkg/runtime/docker.go` creates sparse `.ext4` loopback files per sandbox inside the volumes directory and attaches them alongside `--entrypoint /opt/cubepod/bin/podd-init`. - `podd-init` runs the exact same `blkid` / `e2fsck -p` / `OverlayFS` / `pivot_root` sequence used inside Kata MicroVMs, giving developers high fidelity locally. ## Next Steps - Follow the exact PID 1 boot steps inside the guest in **[In-Guest Runtime (podd-init & podd)](/concepts/in-guest-init-and-podd/)**. - Learn how `PodWarmPool` and `ForkFrom` snapshot dual volumes in **[Lifecycle, WarmPool, CoW Fork & HA](/concepts/lifecycle-warmpool-fork/)**. - Configure CSI storage classes in **[Kata & Block Storage](/deployment/kata-and-block-storage/)**. --- # In-Guest Runtime (podd-init & podd) URL: https://cubepod.cubeplex.ai/concepts/in-guest-init-and-podd/ To support arbitrary user Docker images without rebuilding base images or polluting the user's writable RootFS layer, CubePod splits its in-guest runtime into a dedicated injector image (`images/podd/Dockerfile`) and two statically linked Rust binaries: **`podd-init`** (`crates/podd-init/`) and **`podd`** (`crates/podd/`). ## Zero-Contamination Binary Injection **User sandbox images must never contain `podd-init` or `podd`.** Stock images such as `ubuntu:24.04`, `python:3.12`, or custom desktop/ML images remain completely untouched: 1. `images/podd/Dockerfile` packages compiled `podd-init` and `podd` into a `busybox:1.37-musl` image under `/opt/cubepod/dist/`. 2. **In Kubernetes**: an `initContainer` named `podd-installer` copies `/opt/cubepod/dist/{podd-init,podd}` into a shared Pod `emptyDir` mounted at `/opt/cubepod/bin`. 3. **In Local Docker Loopback**: `pkg/runtime/docker.go` bind-mounts the host binary directory read-only (`-v :/opt/cubepod/bin:ro`) and sets `--entrypoint /opt/cubepod/bin/podd-init`. 4. **Across `pivot_root`**: `podd-init` recursively bind-mounts `/opt/cubepod` from the initial root into the newly pivoted merged root before detaching `.old_root`. Because `/opt/cubepod` lives purely in the ephemeral `emptyDir` / read-only mount rather than the persistent `upperdir`, upgrading CubePod's runtime binaries on Pod resume never leaves stale runtime artifacts inside the user's persistent RootFS volume. ## Why `podd-init` Is Single-Threaded Rust `podd-init` (`crates/podd-init/src/main.rs`) runs as **PID 1** inside the container/MicroVM prior to `pivot_root(2)` and `execvp(3)`: - Linux `pivot_root(2)` and mount-namespace manipulations fail (`EINVAL`) or leak stale filesystem references if multiple OS threads exist inside the calling process. - Go binaries spawn OS runtime scheduler/sysmon threads before entering `main()`, making pure-Go PID 1 `pivot_root` unsafe. - Writing `podd-init` as a tiny, single-threaded, zero-GC Rust binary guarantees deterministic mount setup, filesystem healing, root pivoting, and clean `execvp` handoff into `podd`. ## PID 1 Boot Sequence (`crates/podd-init/src/main.rs`) At container start, `podd-init` reads environment configuration (`CUBEPOD_ROOTFS_DEV` default `/dev/cubepod-rootfs`, `CUBEPOD_WORKSPACE_DEV` default `/dev/cubepod-workspace`, `CUBEPOD_UPPER_MOUNT` default `/mnt/cubepod-upper`, `CUBEPOD_MERGED_ROOT` default `/mnt/cubepod-merged`, `CUBEPOD_UID`/`CUBEPOD_GID` default `1000`, `CUBEPOD_INIT_STATE_PATH` default `/run/cubepod/init-state.json`, and `CUBEPOD_SKIP_PIVOT`) and executes: 1. **Private Mount Propagation**: Calls `make_mount_private("/")` (`MS_PRIVATE | MS_REC`) so mounts inside the guest never propagate back out. 2. **Fallback Check (`volumeMode: Filesystem`)**: If `CUBEPOD_SKIP_PIVOT=1` or the configured block devices do not exist, `podd-init` skips formatting and pivoting, writes `pivoted: false` to `/run/cubepod/init-state.json`, ensures `/workspace` ownership (`1000:1000`), and launches `podd` immediately. 3. **Self-Healing `ext4` Preparation (`ensure_ext4`)**: - Probes `/dev/cubepod-rootfs` and `/dev/cubepod-workspace` with `blkid`. - **First boot (blank block device)**: Formats with `mkfs.ext4 -F -L