# Workers

A Worker is a reusable execution target with its own compute, scaling, isolation, and spending settings. Send independent Worktrees and Sessions to the same Worker; Macrofold places their Runs on available capacity and provisions more within your limits. Files and conversations do not belong to the Worker and survive its shutdown through their verified durable state.

Most applications can omit `worker_id` and use automatic execution. Create a Worker when you want explicit control over compute economics, reuse, concurrency, or baseline availability.

## Start with a Worker

Use your normal authenticated API connection. Inspect the available combinations and rates with `GET /v1/worker-offerings`; each deployment enables only the provider capabilities and prices it supports.

```http
POST /v1/workers
Content-Type: application/json
Idempotency-Key: create-my-worker

{
  "name": "my-worker"
}
```

This uses the deployment's default on-demand sandbox offering, isolated Runs, automatic size selection, and finite effective limits. Read the returned settings and `accepted_offerings` before submitting work. Creating a Worker does not create a Worktree or Session.

Use the returned `id` on a Run:

```http
POST /v1/runs
Content-Type: application/json
Idempotency-Key: first-worker-run

{
  "worktree_id": "YOUR_WORKTREE_ID",
  "worker_id": "YOUR_WORKER_ID",
  "harness": "codex",
  "model": "YOUR_ENABLED_MODEL",
  "billing_mode": "managed",
  "prompt": "Review this project and write a short summary."
}
```

The existing [Run and Session API](https://macrofold.ai/docs/raw/api.md) still determines the execution context. To continue a conversation, supply `session_id` instead of repeating its Worktree and harness configuration:

```json
{
  "session_id": "YOUR_SESSION_ID",
  "worker_id": "YOUR_WORKER_ID",
  "prompt": "Now explain the most important finding."
}
```

A Session can continue on another authorized Worker. `session_id` is a Macrofold ID, not a raw upstream conversation ID. Model/provider compatibility rules still apply.

A Worker with a zero baseline normally reports `sleeping` before its first Run. **Submit the Run to create demand; do not wait for this Worker to become `ready` before submitting.** Observe the accepted Run through its status/events and bounded queue deadline. Reading or polling a Worker does not wake it, and a disconnected client does not cancel already accepted work.

## Match compute ownership to your application

| Calling pattern | Recommended boundary |
| --- | --- |
| Occasional scheduled reviews, webhooks, and personal-agent conversations | Keep automatic execution unless explicit rate or capacity control is needed. Durable files do not require continuously running compute. |
| Frequent interactive turns across independent actors or customers | Share a Worker only across the intended trust boundary; retain separate Worktrees and Sessions. A baseline can reduce cold allocation waits, but is not an instant-start guarantee. |
| Parallel research or coding branches | Use separate Worktrees for simultaneous writers; adding Hosts does not make one Worktree concurrently writable. |
| Short classifications and explicit-context judgments | Use direct inference where appropriate. `/v1/inferences` does not need a native Worker allocation or retained harness conversation. |

The application component that owns shared compute manages pause, resume and destruction. A conversation ending should cancel only its own outstanding Run, not destroy a Worker still used by other conversations. Per-customer data and tool authorization remain independent of shared compute ownership. `isolate_runs: false` is an explicit choice for mutually trusted workloads, not a default for many unrelated customers in one organization.

## Choose the economics

`compute` selects an economic offering, not a provider API. `dedicated` controls exclusive capacity; `isolate_runs` separately controls whether your Runs require isolated execution environments.

| Choice                               | Behavior                                                                          | Charges                                                                                                                             |
| ------------------------------------ | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `compute: "sandbox"`                 | On-demand execution environments. Idle capacity may be released.                  | The accepted allocation or resource rate.                                                                                           |
| `compute: "server", dedicated: true` | Exclusive server-backed capacity, shared by your Runs when `isolate_runs: false`. | Allocated server capacity, including idle time while retained.                                                                      |
| `dedicated: false`                   | No promise of exclusive server capacity. Exact placement is internal.             | The published offering's resource/allocation meter, never an arbitrary share determined by your placement on a nearly empty server. |

Not every deployment offers every combination. Unsupported configurations fail explicitly; Macrofold does not silently switch you to another compute product, region, price, or isolation guarantee. Non-dedicated does not grant other customers access to your files or credentials. It also does not promise that the backend will actually colocate workloads.

### Sustained concurrent traffic

For a game world or continuously busy agent service, keep baseline server capacity and let more capacity be added automatically:

```json
{
  "name": "openlegend",
  "compute": "server",
  "dedicated": true,
  "isolate_runs": false,
  "min_instances": 1,
  "max_instances": 4,
  "max_concurrency": 32,
  "idle_timeout_seconds": 300,
  "max_hourly_compute_cost_micro_usd": "1000000"
}
```

The ceiling above is **an illustrative $1/hour**, not a quoted price or an estimate for this workload. Creation validates that the available rates can fund the requested baseline. Plan limits still apply. `min_instances: 1` retains baseline capacity while enabled; `idle_timeout_seconds` releases excess idle capacity, not the baseline. `null` disables idle shutdown where the selected offering permits it.

For bursts, use `min_instances: 0` with an idle timeout. The Worker remains addressable after its Hosts stop and wakes when new work arrives. A provider's maximum machine lifetime does not change the Worker ID. Use `expires_at` only when you want the Worker itself to expire. Extend or remove that expiration before it passes; an already expired Worker cannot be resumed or reconfigured.

`min_instances` and `max_instances` are dedicated-capacity controls. Non-dedicated callers use resource/concurrency/spending limits without choosing how many machines the pool owns.

## Resource sizing and scaling

Omit `size` on creation, or set `size: null`, to let Macrofold choose among the accepted compatible sizes. A PATCH with `size: null` restores automatic sizing; omitting it preserves the existing choice. The CLI equivalent is `--auto-size`. Advanced callers can select an advertised `size`, `region`, or `runtime`. A Worker may have several backing Hosts, but callers submit to one `worker_id` and never route to machine IDs.

Native Runs targeting an explicit Worker reserve 1,024 MiB and 250 CPU millicores by default. Resource overrides require `worker_id`; automatic execution has its own managed allocation. Override top-level `memory_mib` and `cpu_millis` for known heavier or lighter workloads. These are allocation requirements, not a prediction of future memory use. Macrofold packs compatible Runs within Host headroom and capacity, then adds capacity for queued demand. A single oversized Run requires a large enough allocation; another Host does not enlarge an already-running process.

`max_concurrency` limits active assignments. Cleanup can briefly keep a slot occupied after the public Run becomes terminal, so `occupied_slots` may exceed `active_runs`. Worktree writes remain serialized globally, even when two Runs target different Workers. Use separate Worktrees for independent concurrent writers.

When capacity, credit, or cost limits prevent a start, the Run remains queued within its queue deadline and exposes a waiting reason. Explicit Worker targeting never falls back to unrelated compute.

## Spending controls

Money fields are integer micro-USD strings: `"1000000"` means $1. The CLI accepts ordinary dollar amounts and converts them exactly.

The Worker response shows accepted rates, current committed hourly exposure, reserved funds, and settled compute cost. Starting, draining, and not-yet-confirmed-stopped allocations still count toward commitments. This avoids silently exceeding your ceiling during replacement or shutdown.

The hourly ceiling limits **compute rate**, not monthly spend or model/tool usage. Run budgets and account credit controls remain separate. Model, connector, and tool usage stay attributed to the Run; dedicated compute is charged once at the allocation level rather than once per concurrent Run. Filter [usage](https://macrofold.ai/docs/raw/billing/usage.md) by `worker_id` to inspect compute charges.

Resource-metered offerings use their documented allocated-memory time and CPU usage counters. Missing final measurements are not treated as zero; uncertain charges remain reserved pending reconciliation. Opportunistic warm-process retention is evictable platform cache, not a promise of free dedicated uptime.

Physical shutdown and financial settlement are separate. A confirmed stopped allocation can remain `draining` while cleanup or an unsettled final usage receipt is resolved; held credit is not a final charge. Funding exhaustion must not keep compute running merely because settlement failed. At a passed funding or lifetime boundary, the platform attempts to stop physical execution and preserves unresolved claims and charges. Such a stop can interrupt work and lose unpublished changes. A provider timeout is not proof of a stop, and neither last-seen usage nor an earlier health sample proves an unknown final tail consumed nothing. Operators must reconcile these exceptional states rather than clearing holds or retrying the prompt blindly.

## Pause, resume, destroy, and update

```text
GET    /v1/workers
POST   /v1/workers
GET    /v1/workers/{worker_id}
PATCH  /v1/workers/{worker_id}
POST   /v1/workers/{worker_id}/pause
POST   /v1/workers/{worker_id}/resume
POST   /v1/workers/{worker_id}/destroy
```

Pause stops admissions, lets active Runs finish, then releases compute. It returns the desired state promptly; poll the Worker until its observed status is `paused`. Queued work keeps its deadline, and a manual pause never wakes merely because new traffic arrives. Resume enables execution again. This differs from automatic idle sleep, which wakes on demand.

Destroy retires the Worker, cancels queued work, and drains active work. An explicit `{"force": true}` on pause or destroy requests active cancellation; cleanup and settlement still wait for confirmation. Worktrees, Sessions, and their published checkpoints are not deleted.

PATCH uses `expected_revision` from the latest Worker response. Spending and concurrency changes are validated against existing obligations. Changes to compute, tenancy, isolation, region, runtime, or size require the Worker to be fully paused and drained. Host-local caches may disappear after any replacement; verified durable state is restored as needed.

## CLI and dashboard

Use `macrofold worker --help` or the individual Worker command help for configuration flags. The CLI provides Worker creation, listing, inspection, updates, and lifecycle actions; Run and chat commands accept `--worker`. The dashboard's Workers page exposes the same target and lifecycle state. API examples are the authoritative field-level reference; advanced settings remain available through the API even when a simplified UI does not expose them.

CLI name selectors resolve an exact live Worker name. A destroyed Worker remains inspectable by UUID, and reusing its name targets the replacement. Supplying `worker update WORKER_ID --revision N` avoids an extra read; the server still checks the revision and administrative authority. Name lookup requires `workers:read`; a UUID plus an explicit revision can work with an appropriately restricted `workers:write` key.

The creation form prefers isolated execution. Trusted sharing requires an explicit selection; indefinite idle retention is offered only for dedicated capacity. Expired Workers cannot be resumed or reconfigured, but remain available for explicit destruction/cleanup.

## Migrating a Worktree-bound Sandbox caller

The Worker contract replaces `/v1/sandboxes` and Run `sandbox_id`; it is not a compatible rename. Update the server and caller together after draining old allocations. Keep verified Worktree/Session state and financial history; do not rewrite old receipts as Worker charges.

For an OpenLegend-style world, create or select compute at the application/world trust boundary, not once per actor lane. Replace Sandbox creation/readiness/lifecycle calls with Worker operations and send top-level `worker_id` with each native Run. Continue each actor's existing authorized Worktree and Session independently. A zero-baseline Worker wakes from accepted demand, so remove any unconditional pre-submission readiness wait. Ending one actor conversation must not destroy shared world compute. Only the compute owner performs lifecycle changes.

Retain the caller's stored request identity and exact original body when recovering a lost response. Do not silently attach a different Worker, change rates, restart an expired Worker, or replay an uncertain native launch. Add the appropriate Worker-use/read/management scopes without granting additional file or tool authority. Direct inference callers remain independent of this native-compute migration. Deployments must verify the actual caller migration, not infer it from the presence of new SDK methods.

## Permissions and isolation

`workers:use` permits targeting an authorized Worker. `workers:read` permits inspection. `workers:write` permits compute/lifecycle management and requires organization administrative authority. Worker-ID restrictions on API keys are independent from Workspace restrictions. Creating a new Worker requires a key without Worker restrictions; restricted administrative keys can manage only their authorized existing Workers. Permission to use a Worker does not grant access to any resident Worktree, Session, or another Run's tools.

`isolate_runs: true` requires the advertised isolated execution boundary. Current dedicated/shared providers may satisfy this by using one execution environment per active Run, which reduces density. `false` explicitly permits trusted sharing between your Runs; separate directories and process identities are not a promise of hostile-code isolation. Cancellation and cleanup remain Run-scoped in either mode.

Only exact, clean, authorized Worktree revisions may be reused. A warm Session means its compatible harness process is still alive; it is never the sole durable copy of a conversation. Continuation may use a native ID, local harness files, or both. Cache loss causes a cold start, not loss of the last published state. Unpublished changes can still be lost when a machine fails during execution.
