Workers
Enrol, register, list and deregister workers, and the worker protocol: heartbeats and task leases.
List workers
Workers of the tenant; state (comma-separated) and heartbeat_after narrow the page.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
cursor | string | Optional | |
heartbeat_after | string | Optional | |
limit | string | Optional | |
state | string | Optional |
Response
datahas_morenext_cursor
curl "$CIRESK_API/v1/ci/workers" \
-H "Authorization: Bearer $CIRESK_KEY"Register a worker
Register with an enrolment secret.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | Required |
Request body
capacity- the host's capacity vector: what the lease statement fits a task's
resourcesinto, per dimension. absent from older workers, which are fitted by slots alone capacity.cpuscapacity.disk_bytescapacity.memory_bytescapacity.tmp_bytesfactory_idinstance_idlabels- the label set the queue matches on
labels.archlabels.customlabels.disposablelabels.isolation- One of
process,vm labels.memory_byteslabels.oslabels.toolchainslabels.vcpuslabels.view_isolationnamespacewhen a view job's task may run here; the queue leases a task with a view only to a member that says soOne ofnamespacelabels.workspace_warmmemory_per_slot_bytes- the memory one slot stands for: a task's
resources.memoryweighsceil(memory / memory_per_slot_bytes)slots on this worker; absent from older workers, which run every task at weight one under their own cap nameprotocol_versioncrate::PROTOCOL_VERSION; the server accepts it and the one beforeslotsversion
Response
config- the constants of, handed to the worker so they change without a release
config.egressconfig.egress.allowconfig.egress.mode- One of
audit,block config.egress.strict- decision 10: the tenant's
egress_modeisstrict, sent withmode: blockso a worker that predates it runs the tenant's tasks in block; absent from older control planes, which means not strict config.heartbeat_secsconfig.lease_ttl_secsconfig.log_chunk_bytesconfig.log_flush_msconfig.max_log_bytesconfig.poll_wait_secsconfig.toolchain_store- the cell's toolchain key; absent when the cell has no toolchain store and from older control planes, which means every set installs from upstream
config.toolchain_store.key_idconfig.toolchain_store.public_key-----BEGIN PUBLIC KEY-----config_versionprotocol_versionregion- the cell a tenant is pinned to. mirrors
ciresk_std_api::Regionso the api converts a principal's jurisdiction without a second vocabulary.One ofeu,us store_origin- the
httpsorigin of the cell's store bucket, which the worker declares a cell service of its egress client; absent for a store whose origin serves more than this cell's bucket (path-style, a custom endpoint) or presigns nothing, and from an older api token- the
ciw_token, 24 h, rotated through heartbeat token_expires_atwanted_version- the worker version the server wants running; the worker logs it in v1, self-update is m3
worker_id- uuidv7 rendered as
wrk_<32 hex>
curl -X POST "$CIRESK_API/v1/ci/workers" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Idempotency-Key: e26d245b-2054-43e4-b858-90510efcaec8" \
-H "Content-Type: application/json" \
-d '{
"capacity": {
"cpus": 1,
"disk_bytes": 18432000,
"memory_bytes": 68719476736,
"tmp_bytes": 18432000
},
"factory_id": "static",
"instance_id": "i-0a7c41d2e9f3b8c6d",
"labels": {
"arch": "x86_64",
"custom": {
"custom": "acme/platform"
},
"disposable": false,
"isolation": "process",
"memory_bytes": 68719476736,
"os": "linux",
"toolchains": [
"acme/platform"
],
"vcpus": 16,
"view_isolation": "namespace",
"workspace_warm": true
},
"memory_per_slot_bytes": 18432000,
"name": "acme/platform",
"protocol_version": 1,
"slots": 1,
"version": "2026.10.0"
}'Create an enrolment secret
A one-time enrolment secret.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | Required |
Request body
expires_in_secsfactory_idmax_leases- the most leases that registration may take, at least 1: its last lease drains it and the report that ends that lease revokes its token. 1 for a single-use member; absent is no limit
nametrusts- the tiers the registration this secret makes may lease; empty is every tier the worker's kind allows, as before
Response
expires_atsecret
curl -X POST "$CIRESK_API/v1/ci/workers/enrol" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Idempotency-Key: 92c16cf7-01d1-4e67-a071-e3723ab758fe" \
-H "Content-Type: application/json" \
-d '{
"expires_in_secs": 300,
"factory_id": "static",
"max_leases": 1,
"name": "acme/platform",
"trusts": [
"trusted_interactive"
]
}'Set idle target
The pool's idle timer from the factory's window report, pushed to its workers.
Request body
ceiling_slots- the most slots the pool holds at once (its fixed members plus the burst ceiling, times one member's slots), which the tenant's derived concurrency cap follows; absent from an older factory and while no member of the pool has registered yet
cost_cents_per_hour- what the pool's running and booting members cost per hour now, in cents: the api counts it until the next report and answers the month so far, which the factory's budget pacer reads back after a restart; absent from an older factory, which records no spend
factory_idin_window- false outside the pool's schedule; a pool without a schedule reports true
member_prices- what each running member of the pool costs per hour, which the lease statement ranks its slots by; empty from an older factory and for a pool that prices nothing
member_prices[].cents_per_hourmember_prices[].name
Response
idle_stop_secs- the idle timer the pool's workers now run on
month_spend- what the pool has cost this calendar month (UTC) by the rates it reported; absent when the report carried no cost, and from an older cell
month_spend.centsmonth_spend.since- the first report of the month: a pool that began reporting after the month began has no count for the time before it
reasonrecent_submits,default,quiet,outside_scheduleupdated- workers whose row changed and whose
config_versionwas bumped by this call
curl -X POST "$CIRESK_API/v1/ci/workers/idle-target" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"ceiling_slots": 1,
"cost_cents_per_hour": 1,
"factory_id": "static",
"in_window": true,
"member_prices": [
{
"cents_per_hour": 1,
"name": "acme/platform"
}
]
}'Get a worker
One worker with its workspaces.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Response
busy- work beside its leases at its last heartbeat: a dev session held or starting; the factory's idle backstop counts it as busy
capacity- the capacity vector advertised at registration and what is left of it; absent for a worker that registered without one and is fitted by slots alone
capacity.cpuscapacity.disk_bytescapacity.memory_bytescapacity.tmp_bytesconfig- the worker protocol constants and the tenant's egress policy, as handed out at registration
config.egressconfig.egress.allowconfig.egress.mode- One of
audit,block config.egress.strict- decision 10: the tenant's
egress_modeisstrict, sent withmode: blockso a worker that predates it runs the tenant's tasks in block; absent from older control planes, which means not strict config.heartbeat_secsconfig.lease_ttl_secsconfig.log_chunk_bytesconfig.log_flush_msconfig.max_log_bytesconfig.poll_wait_secsconfig.toolchain_store- the cell's toolchain key; absent when the cell has no toolchain store and from older control planes, which means every set installs from upstream
config.toolchain_store.key_idconfig.toolchain_store.public_key-----BEGIN PUBLIC KEY-----config_version- bumped whenever
configchanges; a heartbeat answering a higher number makes the worker re-read this resource disk_free_bytesfactory_idfirst_registered_at- when the row first registered, the age of the member's disk, which the factory retires past
max_member_age_days; absent from an older cell free- what a worker has (its
capacity, advertised at registration) or has left (free, in every poll and heartbeat): the same four axes asResources, every one present.disk_bytesandtmp_bytesare the volumes' free space minus the worker's floors free.cpusfree.disk_bytesfree.memory_bytesfree.tmp_bytesid- uuidv7 rendered as
wrk_<32 hex> idle_stop_secs- how long a factory-started worker idles before it stops itself;
Nonefor static workers instance_idlabels- the label set the queue matches on
labels.archlabels.customlabels.disposablelabels.isolation- One of
process,vm labels.memory_byteslabels.oslabels.toolchainslabels.vcpuslabels.view_isolationnamespacewhen a view job's task may run here; the queue leases a task with a view only to a member that says soOne ofnamespacelabels.workspace_warmlast_heartbeat_atleases_total- the leases every registration of this row took: the factory retires a member past its pool's
max_member_leases; absent from an older cell, which means none memory_per_slot_bytes- the memory one slot stands for; absent for a worker that registered without it and runs every task at weight one
namepublic_cache- the worker's shared cache of public registry artefacts, from its last heartbeat
public_cache.bytespublic_cache.hitspublic_cache.missesregion- the cell a tenant is pinned to. mirrors
ciresk_std_api::Regionso the api converts a principal's jurisdiction without a second vocabulary.One ofeu,us registered_atslotsslots_freestate- One of
online,draining,offline,deregistered vcpusversion- the release the worker runs, from its registration and refreshed by every heartbeat
wanted_version- the release the server wants this worker on (
RegisterResponse::wanted_version); absent while no update names one workspacesworkspaces[].base_digestworkspaces[].bytesworkspaces[].id- uuidv7 rendered as
ws_<32 hex> workspaces[].kind- One of
warm,ephemeral workspaces[].last_used_atworkspaces[].path- never leaves the cell beyond the owning tenant's own listing
workspaces[].repo_id- uuidv7 rendered as
repo_<32 hex> workspaces[].state- One of
ready,in_use,dirty,evicting,gone workspaces[].trust- tiers, with no order on purpose (amendment 2026-10-27):
trusted_ciis protected-branch automation and the cache writer every tier reads,trusted_interactivea person's own work, and neither is "below" the other, so every rule over tiers names the tiers it meansOne ofuntrusted,trusted_ci,trusted_interactive workspaces[].worker_id- uuidv7 rendered as
wrk_<32 hex>
curl "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344" \
-H "Authorization: Bearer $CIRESK_KEY"Start a bundle
The grant to bundle one warm slot now, or why there is none.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
archdata_dirrepo_id- uuidv7 rendered as
repo_<32 hex> rustslottree_root- the root digest of a working tree: sha-256 of the root directory manifest
Response
granted- which bundle: the slot of a repository's warm root on one architecture, and its generation
granted.archstd::env::consts::ARCHof the producer (aarch64,x86_64)granted.generationgranted.repo_id- uuidv7 rendered as
repo_<32 hex> granted.slotreason
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/bundles" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "x86_64",
"data_dir": "acme/platform",
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"rust": "acme/platform",
"slot": 1,
"tree_root": "sha256:f6d6c911b491012f14dea3f3004b6101280793ee31519604aecd29f823cca9db"
}'Get bundle chunks
One page of presigned gets of a warm-state bundle's chunks.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
archstd::env::consts::ARCHof the producer (aarch64,x86_64)generationrepo_id- uuidv7 rendered as
repo_<32 hex> slotfrom
Response
chunkschunks[].digest- sha-256 digest as
sha256:<64 hex> chunks[].download- a presigned get
chunks[].download.expires_atchunks[].download.urlchunks[].indexchunks[].sizenext- the
fromof the next page; none after the last chunk
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/bundles/chunks" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "x86_64",
"generation": 1,
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"slot": 1,
"from": 1
}'Commit a bundle
Every chunk landed: the granted bundle becomes the slot's current one.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
archstd::env::consts::ARCHof the producer (aarch64,x86_64)generationrepo_id- uuidv7 rendered as
repo_<32 hex> slotchunkschunks[].digest- sha-256 digest as
sha256:<64 hex> chunks[].sizeentriesranran[].nameran[].rununpacked_bytes
Response
bundle- which bundle: the slot of a repository's warm root on one architecture, and its generation
bundle.archstd::env::consts::ARCHof the producer (aarch64,x86_64)bundle.generationbundle.repo_id- uuidv7 rendered as
repo_<32 hex> bundle.slot
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/bundles/commit" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "x86_64",
"generation": 1,
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"slot": 1,
"chunks": [
{
"digest": "sha256:c21fa32a40d82233c44e76e6ae776b4d403ac1a564ca26de95a14c6bb47eaf49",
"size": 18432000
}
],
"entries": 1,
"ran": [
{
"name": "acme/platform",
"run": "acme/platform"
}
],
"unpacked_bytes": 18432000
}'Start a chunk upload
A presigned put for one chunk of a warm-state bundle the worker was granted.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
archstd::env::consts::ARCHof the producer (aarch64,x86_64)generationrepo_id- uuidv7 rendered as
repo_<32 hex> slotdigest- sha-256 digest as
sha256:<64 hex> indexsize
Response
expires_atheadersmethodurl
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/bundles/parts" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "x86_64",
"generation": 1,
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"slot": 1,
"digest": "sha256:c21fa32a40d82233c44e76e6ae776b4d403ac1a564ca26de95a14c6bb47eaf49",
"index": 1,
"size": 18432000
}'Deregister a worker
Deregister, optionally draining.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
drainrevoke- the worker is no longer trusted: every cache-claim key it held is revoked, so no cache row it signed answers again; without it a deregistered worker's rows keep answering, since they were signed while it was trusted
Response
state
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/deregister" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"drain": true,
"revoke": true
}'Send a heartbeat
Liveness, lease renewal, cancellation, token rotation.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
busy- the worker holds work its leases do not show: a dev session held or starting, so the factory does not stop it as idle; absent from older workers
disk_free_bytesdraining- the worker takes no new lease: its host has a spot rebalance recommendation; the api drains its row, so the factory replaces it and ends it once empty. absent from older workers
free- what the worker has left of its
capacity; advisory for cpus and memory, the row's own arithmetic decides, and the measured word for disk and tmp free.cpusfree.disk_bytesfree.memory_bytesfree.tmp_bytesleasesleases[].id- uuidv7 rendered as
lease_<32 hex> leases[].log_bytesleases[].phase- One of
leased,running leases[].renewals_failed- how many renewals of this lease failed on the api's side (5xx, timeout) since the last
one that landed; the api stamps
renewal_failed_atso an expiry in that window is the outage's, not the worker's, and keeps the task's attempt. absent from older workers leases[].task_id- uuidv7 rendered as
tsk_<32 hex> public_cache- the worker's public registry cache; absent from older workers and when the cache is off
public_cache.bytespublic_cache.hitspublic_cache.missesrefresh_tokenregistration- what this process runs with, sent on its first beats until one lands: a worker that resumed from
state.jsonnever registers again, so without it the row kept the labels, slots and capacity of the first registration (eu1 1.0.14: 31 cpus after the release raised them to 38). absent from older workers and on every later beat registration.capacity- what a worker has (its
capacity, advertised at registration) or has left (free, in every poll and heartbeat): the same four axes asResources, every one present.disk_bytesandtmp_bytesare the volumes' free space minus the worker's floors registration.capacity.cpusregistration.capacity.disk_bytesregistration.capacity.memory_bytesregistration.capacity.tmp_bytesregistration.labels- the label set the queue matches on
registration.labels.archregistration.labels.customregistration.labels.disposableregistration.labels.isolation- One of
process,vm registration.labels.memory_bytesregistration.labels.osregistration.labels.toolchainsregistration.labels.vcpusregistration.labels.view_isolationnamespacewhen a view job's task may run here; the queue leases a task with a view only to a member that says soOne ofnamespaceregistration.labels.workspace_warmregistration.memory_per_slot_bytesregistration.signing_key- standard base64 of the ed25519 public key this worker process signs its cache claims with: drawn at boot, so a restart rotates it; absent from older workers, whose cache rows the api no longer records
registration.slotsslots_freestopping- the worker is on its way out (an idle stop, a shutdown signal, a spot interruption notice) and
leases nothing more: the api takes an
onlinerowofflinenow, so the factory and the lease statement stop counting its slots before the reaper's 90 s of silence. absent from older workers version- the release the worker runs (
ciresk_ci_core::release_string), so a self-updated worker refreshes its row without re-registering; absent from older workers
Response
config_versionlease_pipeline- the api frees a lease's slot, cpus and memory at its
process_endedbeat and answersmax_leasesandReportRequest::next; absent from an older api, against which the worker counts an ended lease as held until its report leasesleases[].expires_atleases[].id- uuidv7 rendered as
lease_<32 hex> leases[].stateactiverenewed,cancel_requestedmeans stop and reportcancelled,expiredmeans kill and report nothingOne ofactive,cancel_requested,expiredleases[].write_token- re-issued once 80 % of the write token's lifetime has passed
store_origin- see
RegisterResponse::store_origin: on every beat, so a worker resumed fromstate.jsonlearns it from its first beat of the boot token- a new
ciw_token; the old one stays valid five minutes token_expires_atwanted_version- see
RegisterResponse::wanted_version
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/heartbeat" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"busy": true,
"disk_free_bytes": 18432000,
"draining": false,
"free": {
"cpus": 1,
"disk_bytes": 18432000,
"memory_bytes": 68719476736,
"tmp_bytes": 18432000
},
"leases": [
{
"id": "lease_01778d67b48ee630f44b3f03ab6254a8",
"log_bytes": 18432000,
"phase": "leased",
"renewals_failed": 1,
"task_id": "tsk_011ec0cc99550d0e90bc9fed5781decd"
}
],
"public_cache": {
"bytes": 18432000,
"hits": 2,
"misses": 1
},
"refresh_token": true,
"registration": {
"capacity": {
"cpus": 1,
"disk_bytes": 18432000,
"memory_bytes": 68719476736,
"tmp_bytes": 18432000
},
"labels": {
"arch": "x86_64",
"custom": {
"custom": "acme/platform"
},
"disposable": false,
"isolation": "process",
"memory_bytes": 68719476736,
"os": "linux",
"toolchains": [
"acme/platform"
],
"vcpus": 16,
"view_isolation": "namespace",
"workspace_warm": true
},
"memory_per_slot_bytes": 18432000,
"signing_key": "acme/platform",
"slots": 1
},
"slots_free": 1,
"stopping": true,
"version": "2026.10.0"
}'Start a slot image
The grant to refresh one slot image lineage now, or why there is none.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
archgidlineagerepo_id- uuidv7 rendered as
repo_<32 hex> rusttree_root- the root digest of a working tree: sha-256 of the root directory manifest
uidworkspace_path
Response
granted- which image: one lineage (the producing slot's index) of a repository on one architecture, and its generation
granted.archstd::env::consts::ARCHof the producer (aarch64,x86_64)granted.generationgranted.lineagegranted.repo_id- uuidv7 rendered as
repo_<32 hex> reason
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/images" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "x86_64",
"gid": 1,
"lineage": 1,
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"rust": "acme/platform",
"tree_root": "sha256:f6d6c911b491012f14dea3f3004b6101280793ee31519604aecd29f823cca9db",
"uid": 1,
"workspace_path": "acme/platform"
}'Get slot image chunks
Presigned gets for slot image chunks of the worker's tenant, by digest.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
archdigests- raw-byte digests, at most
IMAGE_GETS_MAX repo_id- uuidv7 rendered as
repo_<32 hex>
Response
chunks- in the request's order
chunks[].digest- sha-256 digest as
sha256:<64 hex> chunks[].download- a presigned get
chunks[].download.expires_atchunks[].download.url
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/images/chunks" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "x86_64",
"digests": [
"sha256:c21fa32a40d82233c44e76e6ae776b4d403ac1a564ca26de95a14c6bb47eaf49"
],
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b"
}'Commit a slot image
Make a granted slot image current once its manifest and chunks are in the store.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
archstd::env::consts::ARCHof the producer (aarch64,x86_64)generationlineagerepo_id- uuidv7 rendered as
repo_<32 hex> manifest- an object of the store by its sha-256 and size: the manifest, or a stored chunk
manifest.digest- sha-256 digest as
sha256:<64 hex> manifest.sizeranran[].nameran[].run
Response
image- which image: one lineage (the producing slot's index) of a repository on one architecture, and its generation
image.archstd::env::consts::ARCHof the producer (aarch64,x86_64)image.generationimage.lineageimage.repo_id- uuidv7 rendered as
repo_<32 hex> new_bytesnew_chunks- the chunks this generation stored first, and their stored bytes: what the refresh uploaded
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/images/commit" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "x86_64",
"generation": 1,
"lineage": 1,
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"manifest": {
"digest": "sha256:c21fa32a40d82233c44e76e6ae776b4d403ac1a564ca26de95a14c6bb47eaf49",
"size": 18432000
},
"ran": [
{
"name": "acme/platform",
"run": "acme/platform"
}
]
}'Start slot image uploads
Presigned puts for a granted slot image's chunks the tenant lacks, and its manifest.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
archstd::env::consts::ARCHof the producer (aarch64,x86_64)generationlineagerepo_id- uuidv7 rendered as
repo_<32 hex> chunks- at most
IMAGE_PARTS_MAX chunks[].digest- sha-256 digest as
sha256:<64 hex> chunks[].stored- an object of the store by its sha-256 and size: the manifest, or a stored chunk
chunks[].stored.digest- sha-256 digest as
sha256:<64 hex> chunks[].stored.sizemanifest- an object of the store by its sha-256 and size: the manifest, or a stored chunk
manifest.digest- sha-256 digest as
sha256:<64 hex> manifest.size
Response
manifest- a presigned put
manifest.expires_atmanifest.headersmanifest.methodmanifest.urlparts- in the request's order
parts[].digest- sha-256 digest as
sha256:<64 hex> parts[].upload- a presigned put
parts[].upload.expires_atparts[].upload.headersparts[].upload.methodparts[].upload.url
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/images/parts" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "x86_64",
"generation": 1,
"lineage": 1,
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"chunks": [
{
"digest": "sha256:c21fa32a40d82233c44e76e6ae776b4d403ac1a564ca26de95a14c6bb47eaf49",
"stored": {
"digest": "sha256:c21fa32a40d82233c44e76e6ae776b4d403ac1a564ca26de95a14c6bb47eaf49",
"size": 18432000
}
}
],
"manifest": {
"digest": "sha256:c21fa32a40d82233c44e76e6ae776b4d403ac1a564ca26de95a14c6bb47eaf49",
"size": 18432000
}
}'Report a slot image trace
Merge a mounted slot image's first-read order into its lineage's trace.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
archstd::env::consts::ARCHof the producer (aarch64,x86_64)generationlineagerepo_id- uuidv7 rendered as
repo_<32 hex> chunks
Response
kept- the lineage's trace length after the merge
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/images/trace" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"arch": "x86_64",
"generation": 1,
"lineage": 1,
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"chunks": [
1
]
}'Lease a task
The 30 s long-poll for the tasks that fit.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
boot_to_online_ms- the worker's boot-to-online time, recorded on the lease for the cold start analysis
disk_free_bytesfirst_lease_after_boot- no lease since boot (Neon item 4): the lease is
first_after_bootunless the member came online after the job free- see
HeartbeatRequest::free; a zero in cpus or memory answers204at once free.cpusfree.disk_bytesfree.memory_bytesfree.tmp_bytesmax_leases- how many leases one answer may carry, the worker's free slots; absent from an older worker: one
process_ended- the phase beat
process_ended: the leases this worker holds whose process exited and whose report has not landed; the api frees their slot, cpus and memory before it leases. absent from an older worker slots_freewait_secswarmwarm[].base_digestwarm[].repo_id- uuidv7 rendered as
repo_<32 hex>
Response
jobjob.actorjob.base_digestjob.base_refjob.diff- a content-addressed blob the receiver fetches by presigned url
job.diff.digest- sha-256 digest as
sha256:<64 hex> job.diff.download- a presigned get
job.diff.download.expires_atjob.diff.download.urljob.diff.sizejob.graph- the cell holds no graph for this root and the manifest has a task with this strategy: record one after the run, once per job
job.graph.dir- where the tool runs, repository-relative,
""for the root job.graph.root- the root digest of a working tree: sha-256 of the root directory manifest
job.graph.strategy- One of
cargo,pnpm,go,turbo,nx,files,none job.graphs- every graph the cell lacks for this root, one request per strategy the manifest declares (
cargo,pnpm,go);graphcarries the first of them for a worker from before this field job.graphs[].dir- where the tool runs, repository-relative,
""for the root job.graphs[].root- the root digest of a working tree: sha-256 of the root directory manifest
job.graphs[].strategy- One of
cargo,pnpm,go,turbo,nx,files,none job.id- uuidv7 rendered as
job_<32 hex> job.kindcheck,test,build,lintor a manifest task idjob.manifest_digest- sha-256 digest as
sha256:<64 hex> job.manifest_path- the governing manifest's repository-relative path; absent means
<cwd>/.ciresk/ci.yaml job.org_idtenants.org_idoftenant, the organisation dev names its sessions by: the worker records it on the warm root, so a dev session only ever pins or indexes its own org's generations; absent from an older api, and a root without it is never handed to a sessionjob.repojob.repo.default_branch- the registration's default branch (
ci.repos.create): a lease whosebase_refnames it is what an overlay member's trunk follows; absent from an older api job.repo.id- uuidv7 rendered as
repo_<32 hex> job.repo.slugjob.repo.urljob.seed_pack- the repository's seed pack, offered only to a job with a tree and no view; absent when the cell has none and from an older api
job.seed_pack.bytesjob.seed_pack.download- a presigned get
job.seed_pack.download.expires_atjob.seed_pack.download.urljob.seed_pack.entriesjob.seed_pack.root- the tree the pack was built from; its root manifest is the pack's last entry, so a cas that holds the root was seeded already
job.sessionjob.targetsjob.tenant- the owner of the tree's blobs, the partition of the worker's local cas
job.toolchainsjob.tree- the tree to materialise;
diffstays until every worker readstree job.tree.bytesjob.tree.entriesjob.tree.root- the root digest of a working tree: sha-256 of the root directory manifest
job.trust- tiers, with no order on purpose (amendment 2026-10-27):
trusted_ciis protected-branch automation and the cache writer every tier reads,trusted_interactivea person's own work, and neither is "below" the other, so every rule over tiers names the tiers it meansOne ofuntrusted,trusted_ci,trusted_interactive job.view- the requester's path view when it hides a prefix: the task runs in a fresh namespace on a tree of the readable paths only; absent for every other job
job.view.digestViewSpec::digest: the entries only, so every key that carries it also carries the tenantjob.view.entriesjob.view.entries[].prefixjob.view.entries[].readjob.view.entries[].writejob.view.policy_versionjob.workspacejob.workspace.keepjob.workspace.kind- One of
warm,ephemeral job.workspace.prewarm- the manifest's
workspace.prewarm: an idle worker may refresh the repo's trunk job.workspace.sparseleaselease.cold_start- how warm this member was for the queue (Neon item 4); absent from an older control planeOne of
warm,started,first_after_boot lease.expected_ms- the task's expected run: on a spot notice a lease whose remainder fits is finished, not handed back; absent from an older control plane
lease.expires_atlease.fencing- equals
tasks.attempt; a report with an older fence is409 lease_not_held lease.heartbeat_secslease.id- uuidv7 rendered as
lease_<32 hex> lease.warm_wait_ms- the task's warm wait: 0 means a warm slot pays nothing, so the task never waits for a restore. absent without both p50s and from an older control plane, which waits as before
lease.weight- how many of the worker's slots this lease holds:
ceil(resources.memory / memory_per_slot), at least one; an older control plane sends none, which is one taskrunarrives withtargets_argalready substitutedtask.artifactstask.attempttask.cache- the namespaces this task reads and the one it may write, with the hmac write token
task.cache.bypass- why the worker computes the key but skips the lookup:
no_cachefor the job's--no-cache,results_offfor the task'scache: { results: false }; absent means look up task.cache.enabledtask.cache.readtask.cache.submit_missed- the submit key the api looked up when the task became ready and found no recorded success under: the worker reads no early key and asks once after the materialise. absent from an older api and whenever the api did not look up or found a row, which means as before
task.cache.writetask.cache.write_tokentask.cwdtask.depends_ontask.depends_on[].content_digest- sha-256 of the outputs tar before compression: what the task's key takes for this dependency, and what
the restore checks; absent from an older api and for a dependency recorded before it, where the key
takes
outputs_digest task.depends_on[].download- a presigned get
task.depends_on[].download.expires_attask.depends_on[].download.urltask.depends_on[].nametask.depends_on[].outputs_digest- sha-256 digest as
sha256:<64 hex> task.depends_on[].task_id- uuidv7 rendered as
tsk_<32 hex> task.egress_strict- decision 10: the manifest's
egress: strict; the worker runs the task strict when this or the tenant's policy says so. absent from older control planes, which means the tenant's mode alone task.envtask.global_inputs- the manifest's
globals.inputs: in every task's key whether or not the task declaresinputs, so a lockfile change never hits a stale verdict; absent from older control planes, which means none task.id- uuidv7 rendered as
tsk_<32 hex> task.id_token- : the task's id token; absent when it asks for none, its job is untrusted, or from older apis
task.id_token.audiencetask.id_token.expires_attask.id_token.token- the signed jwt the task hands a relying party
task.id_token_refused- : why the token the task asks for was refused; the worker fails the task before it runs
task.inputstask.knobs- what the derived tool knobs come from when the api trimmed the grant's memory below the manifest's from this run's history: the untrimmed request, so a trim moves the scope's caps and the fit, never a tool's parallelism or heap. absent when the grant is untrimmed and from older control planes: the knobs follow it
task.knobs.cpus- whole cpus,
CPUQuotaof the scope and the-jevery tool is handed task.knobs.disk- space the task writes to its workspace (a build directory); fitted against the data volume
task.knobs.memoryMemoryMaxof the scopetask.knobs.tmp- scratch space the task needs under
TMPDIR; the worker refuses the run when the data volume has less than this free task.log- the log token path of; absent from older control planes, which means the worker token
task.log.direct- : the worker puts sealed segments into the store itself (
ci.logs.segment_url, thenci.logs.segment_commit) and sends no log bytes to the api; absent from an older control plane, which means every chunk throughci.logs.append task.log.max_bytestask.log.tokentask.nametask.nextest- : the row is one half of a task that builds once and tests on shards; absent for every other task and from older control planes
task.nextest.role- which half of an
archivetask a row isOne ofarchive,shard task.nextest.tasktask.outputstask.pass_envtask.pass_env_refused- the
pass_envnames whose secret scope this job is outside of: never inpass_env, and the worker fails the task naming each one instead of running it without them. absent from older control planes, which means none task.pass_env_refused[].nametask.pass_env_refused[].reasontask.priorities- the tests to start first, for a task with a junit report the cell has results of (CIR-722); absent from older control planes and for every other task
task.priorities.first- failed or flaky in a recent run and not passed since, most recent first
task.priorities.first[].nametask.priorities.first[].suitetask.priorities.slow- the slowest seen, slowest first
task.priorities.slow[].nametask.priorities.slow[].suitetask.reporttask.report.junittask.report.timingstask.resources- the grant: the manifest's
resourceswith what it left unsaid filled from the task's history or the kind defaults; the scope's caps and the derived tool knobs come from it. absent from older control planes, which means the worker's limits task.resources.cpus- whole cpus,
CPUQuotaof the scope and the-jevery tool is handed task.resources.disk- space the task writes to its workspace (a build directory); fitted against the data volume
task.resources.memoryMemoryMaxof the scopetask.resources.tmp- scratch space the task needs under
TMPDIR; the worker refuses the run when the data volume has less than this free task.runtask.services- : the services the task runs beside it, by name, started before it in its own network namespace;
leased only to a member labelled
services: podman, absent for every other task and from older control planes task.shard- a 1-based shard of a sharded task; rendered as
k/Nwhere the wire asks for a string task.shard.counttask.shard.indextask.timeout_secstask.toolchains- : a matrix cell's toolchains when an axis pins one, replacing the job's
toolchainsfor this task; absent for every other task and from older control planes task.trace_inputs- : the worker traces the task's file reads and reports them as
observed_inputs; absent from older control planes, which means off leases- the leases after the first, up to
LeaseRequest::max_leases; absent from an older api leases[].jobleases[].job.actorleases[].job.base_digestleases[].job.base_refleases[].job.diff- a content-addressed blob the receiver fetches by presigned url
leases[].job.diff.digest- sha-256 digest as
sha256:<64 hex> leases[].job.diff.download- a presigned get
leases[].job.diff.sizeleases[].job.graph- the cell holds no graph for this root and the manifest has a task with this strategy: record one after the run, once per job
leases[].job.graph.dir- where the tool runs, repository-relative,
""for the root leases[].job.graph.root- the root digest of a working tree: sha-256 of the root directory manifest
leases[].job.graph.strategy- One of
cargo,pnpm,go,turbo,nx,files,none leases[].job.graphs- every graph the cell lacks for this root, one request per strategy the manifest declares (
cargo,pnpm,go);graphcarries the first of them for a worker from before this field leases[].job.graphs[].dir- where the tool runs, repository-relative,
""for the root leases[].job.graphs[].root- the root digest of a working tree: sha-256 of the root directory manifest
leases[].job.graphs[].strategy- One of
cargo,pnpm,go,turbo,nx,files,none leases[].job.id- uuidv7 rendered as
job_<32 hex> leases[].job.kindcheck,test,build,lintor a manifest task idleases[].job.manifest_digest- sha-256 digest as
sha256:<64 hex> leases[].job.manifest_path- the governing manifest's repository-relative path; absent means
<cwd>/.ciresk/ci.yaml leases[].job.org_idtenants.org_idoftenant, the organisation dev names its sessions by: the worker records it on the warm root, so a dev session only ever pins or indexes its own org's generations; absent from an older api, and a root without it is never handed to a sessionleases[].job.repoleases[].job.repo.default_branch- the registration's default branch (
ci.repos.create): a lease whosebase_refnames it is what an overlay member's trunk follows; absent from an older api leases[].job.repo.id- uuidv7 rendered as
repo_<32 hex> leases[].job.repo.slugleases[].job.repo.urlleases[].job.seed_pack- the repository's seed pack, offered only to a job with a tree and no view; absent when the cell has none and from an older api
leases[].job.seed_pack.bytesleases[].job.seed_pack.download- a presigned get
leases[].job.seed_pack.entriesleases[].job.seed_pack.root- the tree the pack was built from; its root manifest is the pack's last entry, so a cas that holds the root was seeded already
leases[].job.sessionleases[].job.targetsleases[].job.tenant- the owner of the tree's blobs, the partition of the worker's local cas
leases[].job.toolchainsleases[].job.tree- the tree to materialise;
diffstays until every worker readstree leases[].job.tree.bytesleases[].job.tree.entriesleases[].job.tree.root- the root digest of a working tree: sha-256 of the root directory manifest
leases[].job.trust- tiers, with no order on purpose (amendment 2026-10-27):
trusted_ciis protected-branch automation and the cache writer every tier reads,trusted_interactivea person's own work, and neither is "below" the other, so every rule over tiers names the tiers it meansOne ofuntrusted,trusted_ci,trusted_interactive leases[].job.view- the requester's path view when it hides a prefix: the task runs in a fresh namespace on a tree of the readable paths only; absent for every other job
leases[].job.view.digestViewSpec::digest: the entries only, so every key that carries it also carries the tenantleases[].job.view.entriesleases[].job.view.policy_versionleases[].job.workspaceleases[].job.workspace.keepleases[].job.workspace.kind- One of
warm,ephemeral leases[].job.workspace.prewarm- the manifest's
workspace.prewarm: an idle worker may refresh the repo's trunk leases[].job.workspace.sparseleases[].leaseleases[].lease.cold_start- how warm this member was for the queue (Neon item 4); absent from an older control planeOne of
warm,started,first_after_boot leases[].lease.expected_ms- the task's expected run: on a spot notice a lease whose remainder fits is finished, not handed back; absent from an older control plane
leases[].lease.expires_atleases[].lease.fencing- equals
tasks.attempt; a report with an older fence is409 lease_not_held leases[].lease.heartbeat_secsleases[].lease.id- uuidv7 rendered as
lease_<32 hex> leases[].lease.warm_wait_ms- the task's warm wait: 0 means a warm slot pays nothing, so the task never waits for a restore. absent without both p50s and from an older control plane, which waits as before
leases[].lease.weight- how many of the worker's slots this lease holds:
ceil(resources.memory / memory_per_slot), at least one; an older control plane sends none, which is one leases[].taskrunarrives withtargets_argalready substitutedleases[].task.artifactsleases[].task.attemptleases[].task.cache- the namespaces this task reads and the one it may write, with the hmac write token
leases[].task.cache.bypass- why the worker computes the key but skips the lookup:
no_cachefor the job's--no-cache,results_offfor the task'scache: { results: false }; absent means look up leases[].task.cache.enabledleases[].task.cache.readleases[].task.cache.submit_missed- the submit key the api looked up when the task became ready and found no recorded success under: the worker reads no early key and asks once after the materialise. absent from an older api and whenever the api did not look up or found a row, which means as before
leases[].task.cache.writeleases[].task.cache.write_tokenleases[].task.cwdleases[].task.depends_onleases[].task.depends_on[].content_digest- sha-256 of the outputs tar before compression: what the task's key takes for this dependency, and what
the restore checks; absent from an older api and for a dependency recorded before it, where the key
takes
outputs_digest leases[].task.depends_on[].download- a presigned get
leases[].task.depends_on[].nameleases[].task.depends_on[].outputs_digest- sha-256 digest as
sha256:<64 hex> leases[].task.depends_on[].task_id- uuidv7 rendered as
tsk_<32 hex> leases[].task.egress_strict- decision 10: the manifest's
egress: strict; the worker runs the task strict when this or the tenant's policy says so. absent from older control planes, which means the tenant's mode alone leases[].task.envleases[].task.global_inputs- the manifest's
globals.inputs: in every task's key whether or not the task declaresinputs, so a lockfile change never hits a stale verdict; absent from older control planes, which means none leases[].task.id- uuidv7 rendered as
tsk_<32 hex> leases[].task.id_token- : the task's id token; absent when it asks for none, its job is untrusted, or from older apis
leases[].task.id_token.audienceleases[].task.id_token.expires_atleases[].task.id_token.token- the signed jwt the task hands a relying party
leases[].task.id_token_refused- : why the token the task asks for was refused; the worker fails the task before it runs
leases[].task.inputsleases[].task.knobs- what the derived tool knobs come from when the api trimmed the grant's memory below the manifest's from this run's history: the untrimmed request, so a trim moves the scope's caps and the fit, never a tool's parallelism or heap. absent when the grant is untrimmed and from older control planes: the knobs follow it
leases[].task.knobs.cpus- whole cpus,
CPUQuotaof the scope and the-jevery tool is handed leases[].task.knobs.disk- space the task writes to its workspace (a build directory); fitted against the data volume
leases[].task.knobs.memoryMemoryMaxof the scopeleases[].task.knobs.tmp- scratch space the task needs under
TMPDIR; the worker refuses the run when the data volume has less than this free leases[].task.log- the log token path of; absent from older control planes, which means the worker token
leases[].task.log.direct- : the worker puts sealed segments into the store itself (
ci.logs.segment_url, thenci.logs.segment_commit) and sends no log bytes to the api; absent from an older control plane, which means every chunk throughci.logs.append leases[].task.log.max_bytesleases[].task.log.tokenleases[].task.nameleases[].task.nextest- : the row is one half of a task that builds once and tests on shards; absent for every other task and from older control planes
leases[].task.nextest.role- which half of an
archivetask a row isOne ofarchive,shard leases[].task.nextest.taskleases[].task.outputsleases[].task.pass_envleases[].task.pass_env_refused- the
pass_envnames whose secret scope this job is outside of: never inpass_env, and the worker fails the task naming each one instead of running it without them. absent from older control planes, which means none leases[].task.pass_env_refused[].nameleases[].task.pass_env_refused[].reasonleases[].task.priorities- the tests to start first, for a task with a junit report the cell has results of (CIR-722); absent from older control planes and for every other task
leases[].task.priorities.first- failed or flaky in a recent run and not passed since, most recent first
leases[].task.priorities.slow- the slowest seen, slowest first
leases[].task.reportleases[].task.report.junitleases[].task.report.timingsleases[].task.resources- the grant: the manifest's
resourceswith what it left unsaid filled from the task's history or the kind defaults; the scope's caps and the derived tool knobs come from it. absent from older control planes, which means the worker's limits leases[].task.resources.cpus- whole cpus,
CPUQuotaof the scope and the-jevery tool is handed leases[].task.resources.disk- space the task writes to its workspace (a build directory); fitted against the data volume
leases[].task.resources.memoryMemoryMaxof the scopeleases[].task.resources.tmp- scratch space the task needs under
TMPDIR; the worker refuses the run when the data volume has less than this free leases[].task.runleases[].task.services- : the services the task runs beside it, by name, started before it in its own network namespace;
leased only to a member labelled
services: podman, absent for every other task and from older control planes leases[].task.shard- a 1-based shard of a sharded task; rendered as
k/Nwhere the wire asks for a string leases[].task.shard.countleases[].task.shard.indexleases[].task.timeout_secsleases[].task.toolchains- : a matrix cell's toolchains when an axis pins one, replacing the job's
toolchainsfor this task; absent for every other task and from older control planes leases[].task.trace_inputs- : the worker traces the task's file reads and reports them as
observed_inputs; absent from older control planes, which means off
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/lease" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"boot_to_online_ms": 83000,
"disk_free_bytes": 18432000,
"first_lease_after_boot": true,
"free": {
"cpus": 1,
"disk_bytes": 18432000,
"memory_bytes": 68719476736,
"tmp_bytes": 18432000
},
"max_leases": 1,
"process_ended": [
"lease_01d50b6f005647a6f46cd8e6ccc4a47a"
],
"slots_free": 1,
"wait_secs": 300,
"warm": [
{
"base_digest": "feef45d804737fdf7a297e27e443d914551629de",
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b"
}
]
}'Store a registry artefact
A presigned put of one registry artefact the worker fetched and checked, into its tenant's store.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
objectsha256- of the bytes, which the put's checksum binds at the store
size- at most [
REGISTRY_OBJECT_MAX_BYTES]
Response
put- a presigned put
put.expires_atput.headersput.methodput.url
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/registry" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"object": "acme/platform",
"sha256": "sha256:116014cb5ab2f2af478466c8960a4918f44bc9fafe69acac36742f2bde2a0fc5",
"size": 18432000
}'Read registry artefacts
Presigned gets of registry artefacts in the worker's tenant registry store, named by digest.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
objects- objects as [
registry_object_valid] reads them, at most [REGISTRY_LOOKUP_MAX]
Response
getsgets[].download- a presigned get
gets[].download.expires_atgets[].download.urlgets[].object
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/registry/lookup" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"objects": [
"acme/platform"
]
}'Publish a toolchain pack
The grant and a put per chunk to store a pack a trusted task installed, or why not.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
data_dir- the absolute
MISE_DATA_DIRthe installs bake into their paths mise_version- the first line of
mise --version platform<os>-<arch>-<os-release ID>-<VERSION_ID>,linux-aarch64-ubuntu-24.04chunkschunks[].digest- sha-256 digest as
sha256:<64 hex> chunks[].sizecontent- sha-256 digest as
sha256:<64 hex> entriesinstall_user- the install ran as the install user, whose tree no task can write (a root worker's unit); a worker that
installs as its own uid, the uid its tasks run as, sends
falseor nothing and is refusedrunner requestedresolvedtask_id- the lease's task whose install this was: its tier decides whether the store takes it
toolunpacked_bytes
Response
grantputsputs[].expires_atputs[].headersputs[].methodputs[].urlreason
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/toolchains" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"data_dir": "acme/platform",
"mise_version": "2026.10.0",
"platform": "linux-aarch64-ubuntu-24.04",
"chunks": [
{
"digest": "sha256:c21fa32a40d82233c44e76e6ae776b4d403ac1a564ca26de95a14c6bb47eaf49",
"size": 18432000
}
],
"content": "sha256:2dac0aeb3eb528642a9b980efb4608061784f3ad33dc2495133b849f8afec981",
"entries": 1,
"install_user": true,
"requested": "acme/platform",
"resolved": "acme/platform",
"task_id": "tsk_011ec0cc99550d0e90bc9fed5781decd",
"tool": "acme/platform",
"unpacked_bytes": 18432000
}'Commit a toolchain pack
Every chunk landed: the cell signs the granted toolchain pack and makes it the current one.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
grant
Response
key_idsignature
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/toolchains/commit" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"grant": "acme/platform"
}'Read toolchain packs
The cell store's signed toolchain packs for the worker's platform, each with presigned chunk gets.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
data_dir- the absolute
MISE_DATA_DIRthe installs bake into their paths mise_version- the first line of
mise --version platform<os>-<arch>-<os-release ID>-<VERSION_ID>,linux-aarch64-ubuntu-24.04wantswants[].requestedwants[].tool
Response
packs- one per want the store holds; a want without one installs from upstream
packs[].chunkspacks[].chunks[].digest- sha-256 digest as
sha256:<64 hex> packs[].chunks[].download- a presigned get
packs[].chunks[].download.expires_atpacks[].chunks[].download.urlpacks[].chunks[].sizepacks[].content- sha-256 of the uncompressed tar stream
packs[].entriespacks[].key_id- the cell key's id (
ciresk_ci_provenance::PublicKey::key_id) and the base64 ed25519 signature packs[].produced_atpacks[].requestedpacks[].resolvedpacks[].signaturepacks[].toolpacks[].unpacked_bytes
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/toolchains/lookup" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"data_dir": "acme/platform",
"mise_version": "2026.10.0",
"platform": "linux-aarch64-ubuntu-24.04",
"wants": [
{
"requested": "acme/platform",
"tool": "acme/platform"
}
]
}'Undrain a worker
Lease again after a drain.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Response
state
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/undrain" \
-H "Authorization: Bearer $CIRESK_KEY"Get the warm plan
The pool's hot repositories with their seed packs and current warm-state bundles.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Response
repos- hottest first
repos[].bundlesrepos[].bundles[].archstd::env::consts::ARCHof the producer (aarch64,x86_64)repos[].bundles[].generationrepos[].bundles[].repo_id- uuidv7 rendered as
repo_<32 hex> repos[].bundles[].slotrepos[].bundles[].bytes- compressed, the sum of the chunks
repos[].bundles[].chunksrepos[].bundles[].data_dir- the producer's data dir: the slot is restored only at the same absolute path
repos[].bundles[].entries- the tar entries the stream holds; a restore that meets another number refuses the bundle
repos[].bundles[].produced_atrepos[].bundles[].ranrepos[].bundles[].ran[].namerepos[].bundles[].ran[].runrepos[].bundles[].rust- the rust release that built
target/(1.91.0), from its.rustc_info.json; a member without that release installed rebuilds anyway and skips the bundle repos[].bundles[].tree_root- the tree the slot's
tree.idxholds repos[].bundles[].unpacked_bytes- the tar stream's length, what the restore writes about
repos[].demand- the repository's trusted tasks the caller could lease now or soon, and those it holds: a member restores at once at most this many of its slots, the rest only once it has run 10 minutes and only if it ran the repository or it is wanted then. 0 from a server that does not say, which restores a wanted repository whole
repos[].images- the repository's current slot images, lineage 0 first; a member with
slot_imageson mounts these and restores a bundle only into a slot no image fits repos[].images[].archstd::env::consts::ARCHof the producer (aarch64,x86_64)repos[].images[].generationrepos[].images[].lineagerepos[].images[].repo_id- uuidv7 rendered as
repo_<32 hex> repos[].images[].chunks- non-zero chunks, and their raw and stored sums
repos[].images[].gidrepos[].images[].image_bytes- the image's size, holes included
repos[].images[].manifest- an object of the store by its sha-256 and size: the manifest, or a stored chunk
repos[].images[].manifest.digest- sha-256 digest as
sha256:<64 hex> repos[].images[].manifest.sizerepos[].images[].manifest_download- a presigned get
repos[].images[].manifest_download.expires_atrepos[].images[].manifest_download.urlrepos[].images[].produced_atrepos[].images[].ranrepos[].images[].ran[].namerepos[].images[].ran[].runrepos[].images[].raw_bytesrepos[].images[].rust- the rust release that built
target/; a member without it installed skips the image repos[].images[].stored_bytesrepos[].images[].trace- chunk indices in the order earlier first tasks read them, fetched before any other
repos[].images[].tree_root- the root digest of a working tree: sha-256 of the root directory manifest
repos[].images[].uid- the task user and group that own the image's files
repos[].images[].workspace_path- the path the producer's tasks saw the slot at: a member mounts the image only into a slot whose tasks
see the same path (the slot's own path today, the fixed
<warm root>/wsunder CIR-532) repos[].repo_id- uuidv7 rendered as
repo_<32 hex> repos[].seed_pack- one tar of a recent default-branch tree of the lease's repository, its blobs and directory manifests named by digest, so a worker whose cas lacks the tree seeds it with a few ranged gets before it fetches the difference blob by blob. an accelerator: every entry is re-hashed on arrival, and a pack that is absent, too large or broken changes nothing but the number of gets
repos[].seed_pack.bytesrepos[].seed_pack.download- a presigned get
repos[].seed_pack.download.expires_atrepos[].seed_pack.download.urlrepos[].seed_pack.entriesrepos[].seed_pack.root- the tree the pack was built from; its root manifest is the pack's last entry, so a cas that holds the root was seeded already
repos[].task_secondstrusted_interactivetask-seconds over the last 24 hours on the workers of this poolrepos[].tenant- the cas partition the seed pack goes into (the worker's own tenant)
repos[].wanteddemandis above 0. a server that does not say is taken as wanting every repository, as before
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/warm" \
-H "Authorization: Bearer $CIRESK_KEY"Report a worker's workspaces
Workspaces and mirror commits a worker holds.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Required | The worker's ID. |
Request body
commitscommits[].repo_id- uuidv7 rendered as
repo_<32 hex> commits[].shasworkspacesworkspaces[].base_digestworkspaces[].bytesworkspaces[].kind- One of
warm,ephemeral workspaces[].pathworkspaces[].repo_id- uuidv7 rendered as
repo_<32 hex> workspaces[].stalebytesis what the worker recorded at its last walk, not a measurement of now: a worker posts its recorded sizes at boot and walks in the background, so the first lease never waits for a cold-cache stat of every file; the api keeps a row's fresh bytes over a stale reportworkspaces[].state- One of
ready,in_use,dirty,evicting,gone
Response
evict
curl -X POST "$CIRESK_API/v1/ci/workers/wrk_01d0a4beab0d9d6aef88ffcdc85fd344/workspaces" \
-H "Authorization: Bearer $CIRESK_KEY" \
-H "Content-Type: application/json" \
-d '{
"commits": [
{
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"shas": [
"feef45d804737fdf7a297e27e443d914551629de"
]
}
],
"workspaces": [
{
"base_digest": "feef45d804737fdf7a297e27e443d914551629de",
"bytes": 18432000,
"kind": "warm",
"path": ".ciresk/ci.yaml",
"repo_id": "repo_0129354d7b6b4146ad16c315d960365b",
"stale": false,
"state": "ready"
}
]
}'