Compute service
Compute service
Section titled “Compute service”The compute service is the gVisor-backed sandbox data plane for paperboard. It provisions isolated execution environments for every agent tool call, streams exec output to the caller, and emits resource-usage telemetry that feeds the Phase 5 billing engine.
Why a separate service
Section titled “Why a separate service”Agent tool calls run arbitrary code on behalf of tenants. Keeping that code inside the agents process would make it impossible to enforce per-tenant CPU, memory, and disk limits, or to restart a crashed sandbox without losing an in-flight LLM session. A dedicated data-plane service lets us:
- Enforce
runtimeClassName: gvisoron sandbox pods without touching the agents deployment. - Run the gVisor DaemonSet on a dedicated node pool (Phase 6) with independent scaling.
- Expose workspace I/O RPCs (
ReadFile,WriteFile,ListFiles) as first-class operations without adding K8s API client dependencies to agents.
What the service does
Section titled “What the service does”- Creates an isolated gVisor pod + 5 GiB PVC workspace per session on first tool call (lazy provisioning — sessions that never invoke a tool pay nothing).
- Streams exec output (
stdout,stderr) and a finalExecCompletedevent carryingUsageMetricsback to the agents caller. - Exposes workspace I/O RPCs so agents can read/write files across tool-call turns.
- Garbage-collects PVCs after a 24-hour soft-delete grace period.
Who calls compute
Section titled “Who calls compute”| Caller | Protocol | What it asks for |
|---|---|---|
| agents | gRPC | CreateSandbox, ExecCommand, DestroySandbox, file I/O |
| runtime | — | Routes requests; does not call compute directly (Phase 3) |
No public HTTP API. All traffic is internal gRPC (ClusterIP, port 50051).
Sandbox lifecycle
Section titled “Sandbox lifecycle”stateDiagram-v2 [*] --> STARTING : CreateSandbox\n(first tool_use in session) STARTING --> READY : pod Running\n(WaitForRunning) STARTING --> FAILED : pod CrashLoopBackOff\nor deadline exceeded READY --> READY : ExecCommand\n(each tool call) READY --> DESTROYED : DestroySandbox\n(session end or idle timeout) FAILED --> DESTROYED : DestroySandbox DESTROYED --> [*] : PVC soft-deleted\n(24h grace → GC CronJob)A sandbox is lazily created on the first tool_use block from the LLM. Sessions that
complete without invoking any tool have sessions.sandbox_id IS NULL and incur no compute
cost. Subsequent tool calls within the same session re-use the existing sandbox.
Workspace
Section titled “Workspace”Each sandbox gets a dedicated PVC mounted at /workspace:
| Parameter | Default | Override |
|---|---|---|
| Storage class | standard | PVC_STORAGE_CLASS env var |
| Size | 5Gi | PVC_WORKSPACE_SIZE env var |
| Mount path | /workspace | WorkspaceSpec.mount_path in CreateSandboxRequest |
On DestroySandbox the PVC is annotated with compute.paper-board.io/delete-after set to
now + 24h. An hourly GC CronJob (cmd/gc) deletes annotated PVCs past their grace window.
This pattern preserves workspace data for post-mortem inspection after unexpected session ends.
Metering
Section titled “Metering”Every ExecCommand response carries a UsageMetrics message:
cpu_ms wall-clock CPU time consumed by the processmemory_peak_bytes peak RSS during the execmemory_avg_bytes average RSSdisk_read_bytes bytes read from the workspace PVCdisk_write_bytes bytes written to the workspace PVCnetwork_egress_bytes bytes sent to external destinationsnetwork_ingress_bytes bytes received from external destinationsThe agents service writes these into agents.usage_events (Phase 3 outbox) so the platform
service can aggregate quota and billing data in Phase 4.
Key design choices
Section titled “Key design choices”gVisor self-install. The Helm chart ships a DaemonSet that installs runsc on every
node and registers RuntimeClass: gvisor. No GKE Sandbox dependency — the chart is
cloud-agnostic (kind, GCP standard-rwo, AWS gp3).
Lazy sandbox provisioning (D14). Sandboxes are created on first tool_use, not at session start. This avoids paying for a pod + PVC on every LLM turn that has no tool call, which is the common case for conversational agents.
PVC soft-delete grace (D5). A 24-hour window between DestroySandbox and physical PVC
deletion avoids data loss on unexpected session restarts. The CronJob enforces the upper bound.
No Postgres schema (ADR-0002). Compute is stateless — sandbox state lives in K8s pod annotations and labels. The migrator job is a no-op stub present only to satisfy the Helm pre-install/pre-upgrade hook contract.