Concepts
Namespaces, the three axes of a workload, the isolation model, and how failures are classified.
Namespaces
A namespace is the tenancy scope. Every resource — environment, job, sandbox, app, workspace, egress policy, pool — belongs to exactly one, and a namespace-scoped credential cannot name another namespace's resources. Cross-namespace visibility exists only for the admin scope.
The logical boundary is backed by a real one: creating a namespace provisions a dedicated Kubernetes runner namespace (cp-rn-<namespace>) with its own service account, NetworkPolicies, LimitRange, ResourceQuota and gVisor runtime class. Deleting the namespace tears it down. There is no shared runner pool.
| Field | Meaning |
|---|---|
groups | OIDC group → role bindings; how humans get access |
default_limits | Per-workload resource defaults |
quota_cpu_millis / quota_memory_mb / quota_max_pods | Seeds the Kubernetes ResourceQuota |
internal_targets | In-cluster hosts this namespace's policies may reach (Egress) |
image_pull_secrets | Names of platform-held pull secrets copied into this namespace's runner namespace |
Three separate axes
A workload is composed from three independent things. Keeping them apart is what makes caching, forking and rollback cheap.
| Axis | What it is | Mutability |
|---|---|---|
| Environment | Deps and toolchain, as a digest-pinned OCI image | Immutable, content-addressed, shared |
| Workspace | The user's files, via a full file API over object storage | Mutable, durable, outlives any pod |
| Egress policy | Which hosts the workload may reach | Set at create; widened or narrowed live on a sandbox |
A sandbox is environment + workspace + agent. A job is environment + argv (+ optional workspace). An app version is environment + port (+ optional read-only workspace snapshot).
The pod
Every workload pod is built to the same hardened shape.
- gVisor (
runsc) by default. The kernel boundary is a userspace kernel, not the host's.RUNCandKATAare per-environment knobs; running as root is only accepted under gVisor or Kata, never runc. - Hardened by default: non-root uid 1000, read-only root filesystem, all capabilities dropped,
allowPrivilegeEscalation: false, seccompRuntimeDefault, no service-account token,emptyDirscratch with size limits,requests == limits, and anactiveDeadlineSecondskill switch.ContainerSecurityrelaxes only uid and rootfs writability. - An agent sidecar holds the pod's only credentials, materialises the workspace, brokers exec and file IO, seeds inputs, and collects outputs. The workload container itself holds nothing.
- An egress gate init container blocks the workload from starting until the network policy is provably active. A CNI programs per-pod rules shortly after the pod gets its IP; the gate polls direct external egress and only exits once it is sealed. If egress never seals within 40s the pod fails closed and the workload never runs.
- Filesystem layout:
/workis scratch — where job inputs are staged and outputs collected./workspaceis the attached workspace, when there is one.
Network
There is no partial network. The L3 policy is default-deny with exactly one additive rule: TCP to the egress proxy. There is no DNS route (the pod's resolver is 127.0.0.1), no CIDR rule to the internet, no UDP and no QUIC.
Which names resolve and which hosts connect is decided by the egress policy the proxy enforces, per job, per moment. HTTP_PROXY is set for compliant clients, but it is not the boundary — the topology is.
Failure taxonomy
Termination is always derived from pod and cgroup status, never self-reported by the workload.
| Value | Means |
|---|---|
TERMINATION_EXITED | Clean exit; read exit_code |
TERMINATION_OOM_KILLED | Memory limit hit |
TERMINATION_TIMEOUT | timeout_sec / deadline fired |
TERMINATION_SANDBOX_VIOLATION | gVisor killed it (SIGSYS / exit 159), or the egress gate never sealed |
TERMINATION_CANCELLED | CancelJob, or the sandbox went away |
TERMINATION_IMAGE_PULL_FAILED | Image unreachable or not pullable with the namespace's secrets |
TERMINATION_INFRA_ERROR | Platform-side failure |
Identifiers and labels
Ids (job_id, sandbox_id, environment_id, workspace_id, app_id) are the reference and are stable. label is a display name — required by the console, optional on the wire, never interpreted by the platform.
Streaming
The API is unary except where streaming is genuinely needed: RunJob, TailLogs, BuildEnvironment, WatchSandbox, WatchApp, StreamEgressEvents (server streams), and Exec / AttachConsole (bidi). Browsers can't do bidi over connect-web, so the console reaches the bidi calls through WebSocket bridges on the api; native clients use them directly.