Contents

Machine config and native models

Select machine-local configuration, inspect native model availability and read how unattended launches use harness permissions.

Machine config

~/.yoke/config.json holds machine-local connections and tunables: which environment is active, API URLs, local paths. Secrets for capabilities live under ~/.yoke/secrets/ (not in the repo).

A connection's protected boolean means it holds real data, regardless of its name or deployment target. Set it with yoke connection set ENV --protected; clear it with --unprotected. yoke dev db-admin setup accepts the same flags. Direct export/import and disposable database recipes honor this marker. Stored connections using the previous prod key still read as protected; the next config write normalizes that key. The old CLI flags are removed.

Relay service labels and state directories depend only on the canonical config path and connection name. Changing protection cannot move a relay; several protected HTTPS connections can each run their own relay. Installation retires the former shared service only when its document proves the same config, machine home and connection. It preserves the former state directory. Live or unknown worker custody refuses as relay_shared_service_retirement_blocked; close those worker sessions and retry installation. A malformed service document requires ownership repair before retry. A database-admin door still uses its paired HTTPS relay; protection alone does not identify that pairing.

For an isolated machine config, select its home before running a writer:

YOKE_MACHINE_HOME=/tmp/yoke-worker yoke env use local

Configure local in that home first. YOKE_MACHINE_HOME selects config.json inside that directory and takes precedence over YOKE_MACHINE_CONFIG_FILE. Without a home override, the config-file variable selects a file; a relative value anchors under ~/.yoke, then the default is ~/.yoke/config.json. --config selects an explicit file; relative values anchor under the selected machine home.

Writers refuse machine_config_not_owned when the target differs from the selected config or resolves outside an explicit YOKE_MACHINE_HOME, including through a symlink. Remove --config and retry with the selected config. This check runs before config locking or credential changes; readers may inspect an explicit foreign file.

Relay-launched sessions discard an inherited YOKE_MACHINE_CONFIG_FILE for all harnesses and surfaces. They retain the selected machine home, and their child processes inherit that environment. An explicit launcher environment may select a shared config; set its machine home consistently. An isolated worker never uses a steering machine's config-file override implicitly.

connection_projects stores one checkout per project on each named connection:

{"active_env": "hosted", "connection_projects": {
  "hosted": [{"checkout": "/work/hosted-project", "project_id": 7}],
  "self-hosted": [{"checkout": "/work/private-project", "project_id": 7}]
}}

Register with yoke --env hosted project register /work/hosted-project --project-id 7. Connection selection follows --env, then YOKE_ENV, then the deepest registered checkout containing the current directory (including its Git lanes). Without a checkout match, a --project P or PREFIX-N item reference selects its connection when registered on exactly one connection; otherwise active_env remains the fallback. Slugs and prefixes use the existing project inventory on registered connections; an unavailable inventory names the connection and asks for --env ENV. Machine commands without a project reference use the fallback. yoke env use ENV changes that fallback, without changing checkout routing. yoke projects checkout-context reports connection and connection_source after the project identity; --field retains its single-value output. If equally deep checkouts match unrelated connections, selection refuses with checkout_connection_ambiguous. Pass --env ENV, or register the checkout with yoke --env ENV project register CHECKOUT --project-id N --reassign to remove the same checkout from the other connections. Numeric project IDs belong to that connection's universe; the same ID on another connection has its own checkout. A paired HTTPS connection and its database-admin connection share the HTTPS connection's map. An unmapped checkout reports the registration command for the selected connection; it never borrows another map. Removing a connection keeps checkout records. Moving an occupied project's checkout requires --reassign to deliberately replace its routing on the selected connection in this machine's config; --reassign also removes the same checkout from other connection maps.

Reading config never rewrites it. On the first explicit project register, an older flat projects list imports once under the configured active_env, even when the command selects another connection. Set that default to the connection that owns those old IDs before registering. The retained flat list is an inert snapshot for an already installed older reader; new routing never falls back to it, and later registrations update only connection_projects.

Unknown keys warn once per file/key as machine_config_key_unknown and are ignored. Remove the key or use a release that supports it. Invalid known values still refuse; extra settings do not disable commands or harness hooks.

The installer records settings.distribution as {"origin": "https://api.upyoke.com", "channel": "stable"} using the actual install selection. yoke update requires that record and uses it in a fresh shell. Select or repair it explicitly with yoke settings set distribution '{"origin":"URL","channel":"NAME"}'; run that command's --help for flags. This does not change the active control-plane connection. Relays connected to a private API host also require this record and install from origin/simple/; they do not infer an index from the API host. See Maintaining a private fork for artifacts and recovery.

Install and repair commands follow the connected hosted environment. Local and self-hosted connections use settings.distribution.origin; serving processes may declare their origin with YOKE_INSTALL_BASE_URL. Missing origin or channel reports install_origin_unavailable and names yoke settings set distribution instead of choosing Production. Browser onboarding receives its installation command from the engine; an older engine without that field asks its operator to update and restore the distribution origin.

Launch model choice does not live in machine config. An item launch takes the item's workflow stage level and Yoke places it on one of that level's options — see launching by level. A level or an exact selection with --surface and the knobs below is an override. Other unnamed knobs on an explicit model selection take the vendor default. Preview combines equal requested/effective settings and shows both when they differ, with each knob's source (level SENIOR option, explicit launch request, or vendor default). Empty rows are omitted; --json retains the complete record. The bound session shows the effective ask beside provider-attested served facts. A machine file that still carries a retired launch-default key keeps loading; yoke status warns machine_config_key_retired, and the next config write removes the key.

yoke session-control launch preview --model M reports each machine's quota in the pool M actually bills to, under REQUESTED MODEL POOL, and ranks machines by that pool's meter rather than by whichever window reads lowest. The human machine table appears when placement, eligibility or pool readings decided the outcome; all candidates and relays remain available in --json.

yoke session-control launch create --idempotency-key K replays the launch already named by K. If its session ended or was terminated, launch_replay_finished names that launch and says no new worker started. Repeat create with a new key to relaunch. A fresh item-bound create refuses item_has_live_worker while a live session holds its claim or an earlier launch is pending or has a live session. The refusal names the session and launch and leads with yoke session-control session wake SESSION-ID: a worker whose process exited still resumes the same session, claims and lane from its transcript (a desktop session refuses the wake; message it instead). Terminate it only when that wake reports the transcript gone or the resume gets no answer; a worker holding the claim itself is told to release it before launching its own level-change successor. Same-key replay still deduplicates. --context-window accepts a token count or compact form such as 1m. --list-models --surface SURFACE prints this machine's observed native availability (below), plus accepted effort and context values. Claude maps context 1M to the model's [1m] selector and effort to --effort; Codex maps effort to -c model_reasoning_effort=... and refuses explicit context. Cursor passes an exact advertised selector, such as cursor-grok-4.6-high; a separate matching effort is encoded once, and a conflicting effort refuses. A base name plus effort resolves only to an advertised variant. If a selector omits cursor- and the matching prefixed selector is advertised, the refusal suggests it. Cursor context is model-specific: an explicit window requires that exact variant's native display label to name it. Grok has no advertised 1M window; omit the context flag. A label establishes a selectable option, never a served-session measurement, so unattested context stays unknown. Preview and create use the chosen machine's native observations for these checks. An unsupported combination is a preview refusal named for the harness and knob, with a recovery step. A combination the provider rejects at run time fails as model_combo_unsupported, retains a bounded vendor message in launch evidence, and never retries under defaults.

Selectable models are observed, not declared

Which models a surface will accept is a fact about the account and build on one machine, so Yoke reads it from the surface rather than shipping a list. Each relay poll refreshes a per-surface reading and carries it on the heartbeat, so a model an account gains becomes visible to the fleet within about a minute rather than within a release.

Discovery is per surface, and a surface with no adapter says so:

Surface Route
cursor-cli cursor-agent --list-models
codex-cli codex app-server model/list
every other surface none declared; the reading says so by name

Shipping one vendor's CLI adapter proves nothing about the same vendor's desktop app or editor extension, so every known surface gets its own reading rather than inheriting a sibling's.

Each reading names its own status:

Status Meaning
ok the surface answered just now; models is current
stale the last attempt failed; models is what it last published
unsupported Yoke declares no listing adapter for this surface
unknown the surface has never answered

Only ok and stale carry models. Neither unknown nor unsupported is evidence that a model is unavailable — a caller routing work reads the status before the list. A failed probe never empties a list that was populated: it keeps the models with their original observation time and flips the status to stale, because an unreachable surface has not withdrawn anything.

Each model entry carries what its vendor published: the selectable token, a display description, that model's own reasoning options and default, and any replacement target with its retirement time. Reasoning options are per model rather than per surface — one installed app-server offers ultra on its newest model and not on the one beside it.

Availability carries no dependency on researched pricing or tier data. A model this machine can select today is reported today, whether or not anything else is known about it.

Read it three ways:

yoke relay probe-models [--surface S] [--json]   # refresh this machine now
yoke session-control launch preview --list-models  # accepted flags beside availability
yoke steering report get                         # changed sections in held scopes

The relay refreshes on its own cadence during normal polling; yoke relay probe-models is the bounded refresh for when you need the answer before the next poll.

Launched sessions run unattended

A session the launch plane starts is an autonomous worker with nobody watching its terminal, so every launch and every wake engages the harness permission bypass: Claude Code is launched with --dangerously-skip-permissions, Codex CLI with approvals, sandbox, and hook trust bypassed (--dangerously-bypass-approvals-and-sandbox plus --dangerously-bypass-hook-trust; the app-server route uses equivalent thread parameters where available), and Cursor with --force alongside --trust. This is unconditional for launched sessions and changes nothing about a session you start yourself.

One native gate can still refuse: Claude Code declines a bypassed background launch until the machine has accepted the bypass disclaimer once. The launch reports permission_bypass_unaccepted with the recovery step — run claude --dangerously-skip-permissions interactively on that machine, accept the prompt, then retry.

Machine config and native models · Yoke