apps/control/app/api/workers.py.
Before you start
Workers routes are operator routes on the control plane. They authenticate a human’s dashboard session; a virtual key (ob_live_…) authenticates an agent at the gateway and is never accepted here. See Control plane API for how to obtain a session token.
Environment
1
Check what your workspace can actually run
Trimmed
runnable is the answer, not availability. capabilities tells you what your own role permits — render against it rather than guessing.That response is one deployment’s answer, not the shape you should expect. Control asserts no backend by default, so a deployment that has configured nothing reports "backend": "none", "backend_readiness": "not_configured" and "runnable": false on every class, including code and browser. isolation_claim is derived from whichever backend resolved — vendor_attested_microvm for the Obol-operated E2B fleet, development_only for the Docker development backend and the demo backend — and a deployment never gets to state its own. Read what that claim does and does not cover, in full, on Environments.2
Pick a template
{"template": …, "availability": …}. A template gated by an environment class your deployment cannot run is returned with its gate, not hidden — render the gate instead of a start button. A coding template that must run tests, builds or installs is startable when this deployment’s code class is runnable; it is no longer blocked merely because it needs sandbox commands. Control’s availability.runnable is still the only field a start button may read.3
Create the Worker and its Draft v1
A Worker runs as an existing Agent Passport, so
agent_passport_id is required.201
4
Read the draft revision
5
Publish v1
Publishing pins three things, because three different races are being refused: which draft (The SDK reads the digest for you: see below.
revision_id), its exact content (revision_digest), and the published pointer somebody else may have advanced (expected_published_revision_id, null for a first publish).revision_digest is the governed digest control computed for the draft you read. The versions route returns it as draft_governed_digest, and every revision in the list carries its own governed_digest. Send it back unchanged. Never reconstruct it: an edit made after you read the draft changes the digest, so the publish is refused rather than shipping content nobody reviewed (ADR-0130).201
6
Read the cost card
Trimmed
$0.00. The hard stop is always exact, because a run cannot be cancelled at an unknown ceiling. See Control costs.7
Start the run
acknowledged_hard_stop must be exactly the acknowledgement you were just handed.409 with the current card rather than starting a run against a ceiling you did not see:409
GET $WS/worker-runs/{run_id}, which returns its state, its evidence_summary (what the Worker reported, kept apart from what its receipts show) and, if it fails, a typed failure with a fault_domain and a fix action. See Debug a failed run.The same thing with the SDK
run() fetches the estimate, calls confirm, and starts the run with the acknowledgement control just produced. On 409 estimate_stale it calls confirm again with the new card before retrying — an approval of the old ceiling is never reused for a new one. See the SDK reference.
Try it without connecting anything
A demo run uses Obol-owned demo targets: no OAuth, no card, never billed, and labelled a demo everywhere it appears.demo is set by control; a request body has no field for it.
409 demo_revision_missing remains as the backstop for a caller that reached the run path another way.
The seed also creates one reserved passport per workspace, obol-workers-demo, because a Worker runs as an Agent Passport and a fresh workspace has none. It denies everything — no tools, no models, no scopes, destructive actions denied, in every environment — because a demo reaches Obol-owned fixture targets and no system of yours.