[bldr]docs
gRPC APIDaemon

ContainerRuntime

Pods, images, and CID-backed mounts: a docker/podman-style control surface over the container sandbox, for *testing* container images without going through a build. CID in (rootfs + mounts), stdout/stderr + a diff CID out; no host paths.

Pods, images, and CID-backed mounts: a docker/podman-style control surface over the container sandbox, for testing container images without going through a build. CID in (rootfs + mounts), stdout/stderr + a diff CID out; no host paths.

Service builder.ContainerRuntime, 17 rpcs.

PullImage(PullImageRequest) -> stream PullImageEvent

Request: PullImageRequest

message PullImageRequest {
  optional string reference = 1;
  optional string arch = 2;
}
Field
referencee.g. "hello-world", "docker.io/library/busybox:1.36"
archoptional OCI arch (e.g. "amd64"); empty → node's arch

Response: PullImageEvent

Streamed during a pull: the prep progress tree (fetch/cache → per-layer download → untar/merge), then a terminal pulled with the result.

message PullImageEvent {
  oneof event {
    TaskEvent task = 1;
    PullImageResponse pulled = 2;
  }
}

CreatePod(CreatePodRequest) -> CreatePodResponse

Request: CreatePodRequest

message CreatePodRequest {
  optional string rootfs_cid = 1;
  repeated string args = 2;
  repeated string env = 3;
  repeated PodMount mounts = 4;
  repeated PodRlimit rlimits = 5;
  optional string name = 6;
  optional bool tty = 7;
  optional bool internet_access = 8;
  repeated PortForward publish = 9;
  optional string workdir = 10;
}
Field
rootfs_cidimage root filesystem CID
argsargv; args[0] absolute path in the image
env"KEY=VALUE" entries
mounts 
rlimits 
nameoptional human name
ttyAllocate a pseudo-terminal so the process sees a tty (isatty → colored output). stdout+stderr merge into one stream.
internet_accessGive the pod internet egress via a userspace stack (pasta). When set — or when publish is non-empty — the pod runs in its own isolated network namespace (instead of sharing the host network). Default: host network.
publishHost→pod port forwards (also isolates the pod + attaches pasta).
workdirWorking directory override; empty → the image's own WORKDIR (or the rootfs root for a raw rootfs CID).

Response: CreatePodResponse

message CreatePodResponse {
  optional string pod_id = 1;
}

RunPod(RunPodRequest) -> stream PodEvent

Create + start + attach in one streamed call, with a build-style progress tree (image fetch/cache → per-layer download → untar/merge), then a "run" node with the container's logs, then exit.

Request: RunPodRequest

message RunPodRequest {
  optional CreatePodRequest spec = 1;
  optional bool rm = 2;
}
Field
spec 
rmremove the pod after it exits

Response: PodEvent

Streamed while attached / running: prep tree (task) + the run start, output chunks, then a terminal Exited.

message PodEvent {
  oneof event {
    bytes stdout = 1;
    bytes stderr = 2;
    PodExited exited = 3;
    TaskEvent task = 4;
    RunStarted started = 5;
  }
}
Field
stdout 
stderr 
exited 
taskprep progress (pull / untar / merge)
startedcontainer started — the "run" node

StartPod(StartPodRequest) -> StartPodResponse

Request: StartPodRequest

message StartPodRequest {
  optional string pod_id = 1;
}

Response: StartPodResponse

message StartPodResponse {
  // no fields
}

AttachPod(AttachPodRequest) -> stream PodEvent

Request: AttachPodRequest

message AttachPodRequest {
  optional string pod_id = 1;
}

Response: PodEvent

The same message as PodEvent.

KillPod(KillPodRequest) -> KillPodResponse

Request: KillPodRequest

message KillPodRequest {
  optional string pod_id = 1;
  optional int32 signal = 2;
}

Response: KillPodResponse

message KillPodResponse {
  // no fields
}

RemovePod(RemovePodRequest) -> RemovePodResponse

Request: RemovePodRequest

message RemovePodRequest {
  optional string pod_id = 1;
  optional bool force = 2;
}

Response: RemovePodResponse

message RemovePodResponse {
  // no fields
}

ListPods(ListPodsRequest) -> ListPodsResponse

Request: ListPodsRequest

message ListPodsRequest {
  optional bool all = 1;
}

Response: ListPodsResponse

message ListPodsResponse {
  repeated PodInfo pods = 1;
}

WatchPods(WatchPodsRequest) -> stream ListPodsResponse

Watch the whole pod list live: the current list first, then a fresh full list on every create / start / exit / remove. Stays open until cancelled.

Request: WatchPodsRequest

message WatchPodsRequest {
  optional bool all = 1;
}

Response: ListPodsResponse

The same message as ListPodsResponse.

DiffPod(DiffPodRequest) -> DiffPodResponse

Request: DiffPodRequest

Capture selected overlay diffs as FsNode CIDs. Defaults to the rootfs only; mounts names specific mutable mounts to diff instead; all does rootfs + every mutable mount.

message DiffPodRequest {
  optional string pod_id = 1;
  repeated string mounts = 2;
  optional bool all = 3;
}
Field
pod_id 
mountsspecific mutable-mount ids to diff
allrootfs + all mutable mounts

Response: DiffPodResponse

message DiffPodResponse {
  optional string rootfs_diff_cid = 1;
  repeated PodMountDiff mounts = 2;
}
Field
rootfs_diff_cidempty if rootfs wasn't requested
mounts 

GetPodStats(GetPodStatsRequest) -> GetPodStatsResponse

Per-pod cgroup v2 accounting: cumulative CPU time + IO bytes, and current memory / pid count. Rates are derived client-side by differencing samples.

Request: GetPodStatsRequest

message GetPodStatsRequest {
  optional string pod_id = 1;
}

Response: GetPodStatsResponse

message GetPodStatsResponse {
  optional PodStats stats = 1;
}

WatchPodStats(WatchPodStatsRequest) -> stream PodStats

A pod's resource history, then its live samples: the daemon records every pod at 1 Hz for its whole life, so the stream first replays every recorded sample (oldest first, each carrying the time it was taken) and then continues with new ones. A client that connects half-way through a run therefore still sees it from the beginning. Old samples may be thinned — spacing is not uniform, so read each sample's timestamp, never its index. The stream ends when the pod has exited and its history is drained, or when the pod is removed.

Request: WatchPodStatsRequest

message WatchPodStatsRequest {
  optional string pod_id = 1;
  optional uint32 interval_ms = 2;
}
Field
pod_id 
interval_msHow often the server checks for newly recorded samples to deliver; 0 → the 1000ms default. Recording is fixed at 1 Hz by the daemon's per-pod sampler, so values below 1000 are clamped up — they would only add empty polls.

Response: PodStats

A snapshot of one pod's resource use. CPU time, IO bytes and network bytes are cumulative (monotonic — difference successive samples for a rate); memory and pids are instantaneous. available is false when the pod has no per-pod cgroup (cgroup v2 isn't delegated to the daemon), in which case the counters are zero and no history is recorded.

The sources differ per field, and that is deliberate: CPU, memory and pids come from the pod's cgroup, filesystem IO from its FUSE mounts and network bytes from its network namespace. See the per-field notes below for why.

timestamp is when the sample was taken, which for a replayed sample is not when it was sent — plot against it, and derive rates by dividing by the difference between successive timestamps rather than assuming a fixed period.

message PodStats {
  optional string pod_id = 1;
  optional google.protobuf.Timestamp timestamp = 2;
  optional bool available = 3;
  optional uint64 cpu_usage_usec = 4;
  optional uint64 memory_current_bytes = 5;
  optional uint64 memory_peak_bytes = 6;
  optional uint64 io_rbytes = 7;
  optional uint64 io_wbytes = 8;
  optional uint64 pids_current = 9;
  optional uint64 net_rx_bytes = 10;
  optional uint64 net_tx_bytes = 11;
  optional bool net_available = 12;
}
Field
pod_id 
timestamp 
available 
cpu_usage_useccumulative CPU time (µs)
memory_current_bytescurrent memory usage
memory_peak_bytespeak memory usage
io_rbytesCumulative filesystem bytes the pod moved. NOT the cgroup's block-IO counters: a pod's whole filesystem is a FUSE overlay served by the daemon, so the block layer sees the daemon's reads and writes, not the pod's, and cgroup io.stat for a pod is legitimately ~0. These count the bytes crossing the pod's own FUSE mounts, which is what "how much IO did this build do?" actually means here.
io_wbytescumulative bytes written
pids_currentprocess/thread count
net_rx_bytesCumulative bytes on the interfaces of the pod's own network namespace, from the pod's point of view: rx is what it received, tx what it sent. Zero with net_available false when the pod shares the host's network namespace (the default for a build step) — the host's counters are not the pod's, so they are withheld rather than reported as if they were.
net_tx_bytes 
net_available 

CreateMount(CreateMountRequest) -> CreateMountResponse

Request: CreateMountRequest

message CreateMountRequest {
  optional string cid = 1;
  optional string path = 2;
  optional uint32 fetch_timeout_secs = 3;
  optional bool mutable = 4;
}
Field
cid 
path 
fetch_timeout_secsBlock-fetch timeout in seconds. 0 means use the server default (30 s).
mutableMount a writable content-addressed overlay (starts at cid, edits captured via CaptureMount) instead of a read-only view.

Response: CreateMountResponse

message CreateMountResponse {
  optional string id = 1;
  optional string cid = 2;
  optional string path = 3;
}

ListMounts(ListMountsRequest) -> ListMountsResponse

Request: ListMountsRequest

message ListMountsRequest {
  // no fields
}

Response: ListMountsResponse

message ListMountsResponse {
  repeated MountInfo mounts = 1;
}

UpdateMount(UpdateMountRequest) -> UpdateMountResponse

Request: UpdateMountRequest

message UpdateMountRequest {
  optional string id = 1;
  optional string cid = 2;
}

Response: UpdateMountResponse

message UpdateMountResponse {
  optional string id = 1;
  optional string cid = 2;
  optional string path = 3;
}

DeleteMount(DeleteMountRequest) -> DeleteMountResponse

Request: DeleteMountRequest

message DeleteMountRequest {
  optional string id = 1;
}

Response: DeleteMountResponse

message DeleteMountResponse {
  // no fields
}

CaptureMount(CaptureMountRequest) -> CaptureMountResponse

Capture the current content of a mutable mount (optionally at a subpath) as an FsNode CID, optionally tagging it under one or more named refs.

Request: CaptureMountRequest

message CaptureMountRequest {
  optional string id = 1;
  optional string path = 2;
  repeated string refs = 3;
}
Field
idThe mount id (from ListMounts / CreateMount). Must be a mutable mount.
pathOptional path within the mount to capture (default: the whole mount root).
refsOptional named refs to tag the captured CID under (atomic set).

Response: CaptureMountResponse

message CaptureMountResponse {
  optional string cid = 1;
}

Types used above

PullImageResponse

message PullImageResponse {
  optional string image_cid = 1;
  optional string digest = 2;
  optional string rootfs_cid = 3;
}
Field
image_cidthe stored ImageManifest CID
digestdocker-style "sha256:…"
rootfs_cidprepared container rootfs (oci.container-image-root)

PodMount

message PodMount {
  optional string id = 1;
  optional string cid = 2;
  optional string dest = 3;
  optional bool mutable = 4;
}
Field
ididentifier (used to fetch this mount's diff)
cidcontent to mount (an FsNode CID)
destabsolute path inside the container
mutableoverlay (writable, diff capturable) vs. bind (ro)

PodRlimit

message PodRlimit {
  optional int32 resource = 1;
  optional uint64 soft = 2;
  optional uint64 hard = 3;
}
Field
resourcea RLIMIT_* constant
soft 
hard 

PodExited

message PodExited {
  optional int32 exit_code = 1;
  optional string rootfs_diff_cid = 2;
}
Field
exit_code 
rootfs_diff_cidthe container's writable upper layer, as an FsNode

RunStarted

message RunStarted {
  optional string pod_id = 1;
  optional string command = 2;
  optional string image_cid = 3;
  optional string digest = 4;
  optional string rootfs_cid = 5;
}
Field
pod_id 
commandresolved argv
image_cidImageManifest CID (empty when run from a raw rootfs CID)
digestimage digest (empty when run from a raw rootfs CID)
rootfs_cidprepared rootfs CID

PodInfo

message PodInfo {
  optional string id = 1;
  optional string state = 2;
  optional string rootfs_cid = 3;
  optional int32 exit_code = 4;
  optional string rootfs_diff_cid = 5;
  optional int64 created_unix = 6;
  optional string command = 7;
  optional string name = 8;
  optional string log_stream_id = 9;
  optional string log_cid = 10;
  optional PodOwner owner = 11;
  optional string workdir = 12;
  optional string runtime = 13;
  optional string runtime_kind = 14;
  optional string profile_cid = 15;
}
Field
id 
state"created" | "running" | "exited"
rootfs_cid 
exit_codevalid when state == "exited"
rootfs_diff_cidset when exited
created_unix 
commandargs, for display
name 
log_stream_idThe log stream carrying this pod's stdout/stderr, published once the pod starts. Read live via the Logs service (StreamLog).
log_cidThe sealed log Blob CID, set once the pod has exited. Empty while running.
ownerThe resource that created/owns this pod, if any (a run, a build, or a deployment). Extensible like BuildOwner. Absent for a standalone bldr pod.
workdirWorking directory the main process runs in (the image's WORKDIR or an override). Empty → the rootfs root.
runtimeThe container runtime this pod runs on: runtime is the configured runtime id (e.g. "native", "vm"); runtime_kind is "native" | "cloud-hypervisor". Empty on older snapshots.
runtime_kind 
profile_cidCPU-profile Blob CID (collapsed stacks: proc;frame;frame <count>), set once the pod has exited if host-side profiling was on for it. Empty otherwise — which is the normal case, since profiling is opt-in.

PodOwner

The resource that created a pod. Mirrors BuildOwner: a run, build, or deployment can own the pods it spawns, so the UI links a pod to its origin.

message PodOwner {
  optional string kind = 1;
  optional string id = 2;
}
Field
kind"run" | "build" | "deployment"
idthe owning resource's id

PodMountDiff

message PodMountDiff {
  optional string id = 1;
  optional string cid = 2;
}

MountInfo

message MountInfo {
  optional string id = 1;
  optional string cid = 2;
  optional string path = 3;
  optional string build_id = 4;
  optional bool mutable = 5;
}
Field
id 
cid 
path 
build_idSet when this mount is owned by a build (created from a BuildMountTarget). Empty for a user-created mount. A build-controlled mount is refreshed by its build on each successful run and removed when the build is removed; deleting it directly via DeleteMount is discouraged.
mutableA mutable (writable overlay) mount whose content can be captured with CaptureMount; false for a read-only mount.

On this page

PullImage(PullImageRequest) -> stream PullImageEventRequest: PullImageRequestResponse: PullImageEventCreatePod(CreatePodRequest) -> CreatePodResponseRequest: CreatePodRequestResponse: CreatePodResponseRunPod(RunPodRequest) -> stream PodEventRequest: RunPodRequestResponse: PodEventStartPod(StartPodRequest) -> StartPodResponseRequest: StartPodRequestResponse: StartPodResponseAttachPod(AttachPodRequest) -> stream PodEventRequest: AttachPodRequestResponse: PodEventKillPod(KillPodRequest) -> KillPodResponseRequest: KillPodRequestResponse: KillPodResponseRemovePod(RemovePodRequest) -> RemovePodResponseRequest: RemovePodRequestResponse: RemovePodResponseListPods(ListPodsRequest) -> ListPodsResponseRequest: ListPodsRequestResponse: ListPodsResponseWatchPods(WatchPodsRequest) -> stream ListPodsResponseRequest: WatchPodsRequestResponse: ListPodsResponseDiffPod(DiffPodRequest) -> DiffPodResponseRequest: DiffPodRequestResponse: DiffPodResponseGetPodStats(GetPodStatsRequest) -> GetPodStatsResponseRequest: GetPodStatsRequestResponse: GetPodStatsResponseWatchPodStats(WatchPodStatsRequest) -> stream PodStatsRequest: WatchPodStatsRequestResponse: PodStatsCreateMount(CreateMountRequest) -> CreateMountResponseRequest: CreateMountRequestResponse: CreateMountResponseListMounts(ListMountsRequest) -> ListMountsResponseRequest: ListMountsRequestResponse: ListMountsResponseUpdateMount(UpdateMountRequest) -> UpdateMountResponseRequest: UpdateMountRequestResponse: UpdateMountResponseDeleteMount(DeleteMountRequest) -> DeleteMountResponseRequest: DeleteMountRequestResponse: DeleteMountResponseCaptureMount(CaptureMountRequest) -> CaptureMountResponseRequest: CaptureMountRequestResponse: CaptureMountResponseTypes used abovePullImageResponsePodMountPodRlimitPodExitedRunStartedPodInfoPodOwnerPodMountDiffMountInfo