openrig-user
Use when a specific `rig` command, subcommand, or flag is already known and you need its exact syntax, JSON shape, defaults, or error meaning. NOT for natural capability discovery, open-ended how-do-I questions, or choosing which OpenRig move applies.
Try your first task
A source-grounded starter task has not been verified for this skill yet.
SKILL.md
name: openrig-userdescription: >- Use when a specific `rig` command, subcommand, or flag is already known and you need its exact syntax, JSON shape, defaults, or error meaning. NOT for natural capability discovery, open-ended how-do-I questions, or choosing which OpenRig move applies.OpenRig User
This is an as-built guide to the shipped rig CLI.
Use current code and rig ... --help as ground truth if anything here ever conflicts with older planning docs.
Use the OpenRig builder guidance when changing OpenRig behavior, doctrine, or release posture.
Coordination trust boundary
Coordinate as openly as the trust boundary you actually control allows. Within a set of machines you
own that already trust each other — they can reach each other's daemon URLs and the daemon itself
requires no authentication — direct rig send, rig capture, and rig queue should just work.
Don't invent bearer placeholders, allowlists, trust handshakes, or guest-reach restrictions the
environment doesn't require.
Do not add or defend a restriction without naming the protected asset, the actual adversary, the blocked attack path, and the concrete consequence. Missing capability is not protection — repair the route, registration, runtime posture, or product defect directly. Arbitrary boundaries create constraint-induced circumvention: under goal pressure, agents build shadow transports, alternate credentials, and weaker proof instead of the direct authorized path. Keep only explicitly stated external or irreversible boundaries — public push / PR / publish, and destruction of canonical data.
Coordination primitives — when to use which
Three coordination surfaces, used together for forward-momentum work. Internalize this on first read — it shapes every turn you take in a rig.
rig send <seat> "<text>" — intra-pod direct messaging / nudges
Use when you need to ask a quick question or give a teammate context that does not
carry handoff semantics. NOT for durable work. NOT for state that must survive
across turns. The message lands in the target's pane; there is no durable queue
record. The CLI prints Sent to ... (and Verified: yes/no with --verify); read
the receipt and move on.
Example:
rig send dev-reviewer@example-project "The import report is ready; the durable handoff names its evidence." --verify
rig queue create --destination <Y> --tags <...> --body-file <path> — durable work item
Use for any substantive work that must not fall through chat — slice handoffs, guard verdicts, QA results, full-tip reviews, multi-item batches. Survives agent restarts. Tracked in the daemon SQLite schema. Surfaces in Project / queue views
- in the destination seat's inbox. Tag with mission / slice / gate / checkpoint
so future-you (and any peer) can find it. Give the row a
--summary(its subject, wherever the row is listed) and theproject:,mission:andslice:tags when it is project work.
Body discipline: substantive bodies go through --body-file <path> (or - for stdin) — the
purpose-built, corruption-safe surface (it kills the backtick-shell-corruption class for multi-line
bodies). Do NOT inline a backtick-heavy or multi-line body via --body: rig queue create body
parsing breaks on unescaped backticks and rejects flag-like tokens.
Example:
rig queue create \
--destination dev-reviewer@example-project \
--summary "Review the import report: it changes how rows are deduplicated" \
--tags "project:example-project,mission:data-import,slice:import-report" \
--body-file /tmp/import-report-handoff.md
rig queue handoff <qitem-id> --to <next> ... — hot-potato handoff
Use when you have completed your turn on a qitem and the work moves to the next owner. This is forward momentum. The ball passes to the destination seat; chain-of-record (the prior qitem id) is preserved so the verdict trail is intact; tags carry the selected work context forward. Gate tags describe checks actually selected for that work; they do not require a fixed sequence of roles.
Example:
rig queue handoff <qitem-id> \
--to dev-reviewer@example-project \
--summary "Review the import report: it changes how rows are deduplicated" \
--tags "project:example-project,mission:data-import,slice:import-report" \
--body-file /tmp/import-report-handoff.md
After a queue write — use the response; read back only when it doesn't show what you need
A queue write (create, claim, update, handoff, handoff-and-complete) answers with the row it wrote; a
handoff answers with the closed row and its successor. When that answer shows what you need to establish, use it.
create also prints Queue create request ID: <id> (not proof of persistence) on stderr before it sends the write,
so an interrupted create can be reconciled. That ID names a request, not a row. The row is the qitemId on stdout
when the command exits 0. A non-zero exit means no row was confirmed, even when stdout carries a qitemId: an
indeterminate answer repeats the request ID so you can reconcile by it. Don't merge stderr into stdout and keep the
first qitem-… you find there; a refused create prints its request ID first, too.
Read back (rig queue show <id> --full --json, or rig queue transitions <id>) when the answer doesn't show what you
need, for example:
- the outcome was INDETERMINATE (a transport failure, or a 502
remote_queue_write_failedthat may have reached the other host), or the write warnedqitem_body_not_saved(an existing row came back). Reconcile by the original ID before any retry. A 502 whosefailureClassisunknown-host,unsupported-transportorregistryisn't indeterminate: the daemon refused before contacting any host and wrote nothing (createsays "No row was created").unsupported-transportmeans that host entry is SSH-declared. Use the http entry for the same machine fromrig host list; if there isn't one, add it with that machine's daemon URL (rig host add --id <id> --transport http --url <daemon-url>), asking whoever runs that host for the URL; - the output was compact or truncated and the field you need isn't in it (the compact receipt shows
bodyBytes, not the body, and its footer names what it left out); - you need a transition note, a closure or blocker detail, or a successor that the output you saw omits;
- you need evidence from downstream of the write: the recipient's wake (
create --verifywaits for it), or its pickup or claim (a later read); - your team's custody rule asks for a separate read-back.
Don't read back only to see again a field the response already showed you.
§1b doctrine — turn ends by passing the ball
A turn ends by passing the ball, never by going idle holding the slice waiting
on a confirmation the selected process does not include. Follow the current
mission-slice-sop: proportional owner checks are the default; independent review
runs when selected, at the authored work boundary. Role names do not add per-commit
guard, QA, or orchestration gates. Do the authorized work, run its selected checks,
and return the outcome through durable custody.
Valid pauses are only:
- A genuine blocker — file a blocked-state qitem against the blocking peer or surface explicitly to orch.
- A scope-or-architecture question that requires owner input and changes the plan — surface to orch with the specific decision needed.
Implementing already-authorized work is neither of these. Proceed without phantom-gating on an imagined "next prompt" or "operator confirmation" that the process does not require.
Anti-patterns
- Using
rig sendfor durable work → userig queue createinstead. Sends do not survive restarts and do not show up in queue/project views. - Idle-holding a slice for an imagined "next prompt" or "operator confirmation"
that the process does not require → pass the ball via
rig queue handoffand proceed to the next slice or stand by for the inbound verdict. See the §1b doctrine above. - Inlining a multi-line / backtick-heavy body into
rig queue create --body→ use--body-file /tmp/<descriptive-name>.txt(or-for stdin), the corruption-safe surface. The body parser does not tolerate raw backticks or flag-like tokens inline.
Runtime-Gated Coordination Primitives
OpenRig v0.3.1 is published publicly as @openrig/cli@0.3.1 and GitHub Release
v0.3.1. It includes the bundled PL-004 Coordination Primitive System: Phase A
rig stream / rig queue, Phase B rig project / rig view, Phase C
rig watchdog, and Phase D rig workflow / workflow-keepalive.
These are shipped product surfaces in v0.3.x, but they require a compatible
v0.3.x daemon and matching SQLite schema at runtime — the installed package
version is not automatically the version of the daemon serving you. If a
coordination command behaves unexpectedly, confirm the running daemon with
rig whoami --json and daemon status before assuming a product bug.
Default posture:
- Treat daemon
rig queue,rig stream,rig project,rig view,rig watchdog, andrig workflowas the product coordination surfaces when the active daemon is v0.2.0 or newer. - Use daemon-backed
rig queuefor durable routing.update / show / listcomplementcreate / handofffor inspection and state changes; records in an unrelated store are not evidence that this daemon owns the work. - If a daemon-backed coordination command fails, debug the command/runtime/schema edge directly; do not assume the right workaround is to drop back to a config-layer primitive.
- Do not perform daemon stop/start, production DB copy/mutation, release, publish, or other consequence-boundary actions unless the operator/workstream has granted that specific gate.
First-user workspace setup
When booting into a rig on a host where the workspace is unset, gap-ridden, or points at a stale layout, address that before substantive project work. The shipped surface is small + bounded — reach for the canonical commands rather than improvising.
Detect workspace state at boot
Agent-actionable when the daemon is reachable.
rig workspace validate --json
rig workspace validate <path> --kind <user|project|knowledge|lab|delivery> --json
rig workspace validate walks the workspace root and emits a structured
frontmatter-gap report against the v0 contract. Exit code is non-zero when
gaps exist (operators chain into hygiene fix loops). Default root is the
current directory; pass a positional path to validate elsewhere. --kind
scopes the contract to a specific workspace kind; omit for a kind-agnostic
structural check.
If rig workspace validate reports a non-zero gapCount OR the workspace
root is unset / unwritable, the workspace needs instantiation — see the next
section.
Instantiate the canonical workspace scaffold
Agent-actionable. The operation is additive and preserves existing files.
rig config init-workspace
rig config init-workspace --root <path>
rig config init-workspace --dry-run --json
rig config init-workspace scaffolds the canonical workspace layout at the
configured workspace.root (default ~/.openrig/workspace):
missions/— release missions + slicesexhaust/— project-local coordination exhaustSPEC.md— project intentproject.yaml— project catalog selections and mission rootworkspace.yaml— project registration.gitignore— local OpenRig state and exhaust exclusions
--root <path> targets a non-default root for this call; --dry-run reports
what would be created without writing. --force is deprecated compatibility
and still preserves existing files.
Redirect the workspace root
Operator-gated when persistent. Agent-actionable when one-shot via env-var.
For a single command:
OPENRIG_WORKSPACE_ROOT=<path> rig <command> ...
For a persistent host-level redirect, the operator changes the config file or runs the setter:
rig config set workspace.root <path>
ConfigStore precedence: OPENRIG_WORKSPACE_ROOT env > config-file
workspace.root > built-in default ~/.openrig/workspace. The same
precedence governs OPENRIG_WORKSPACE_SPECS_ROOT → workspace.specs_root
(default <workspace_root>/specs).
Prefer the env-var form for one-shot redirects (transparent to operators);
reserve rig config set for changes the operator owns.
Build a workspace from scratch
Agent-actionable. Same surface as the canonical scaffold above; the
workspace.root cascade handles non-existent host paths.
rig config init-workspace --root /path/to/new/workspace
The command additively creates any missing canonical entries and preserves
every existing one; only a complete six-entry scaffold is a no-op. Run
rig workspace validate /path/to/new/workspace --json after to confirm the
contract holds.
Create a workflow inside an existing workspace
Authoring is operator-or-agent; validation + instantiation are agent-actionable.
Workflow spec files live at:
<workspace_root>/specs/workflows/<name>.yaml
<workspace_root> resolves via the ConfigStore precedence named above.
There is no rig workflow create verb in v0.3.x — the spec YAML is authored
directly. Template by hand from the documented schema, or copy a built-in
starter from <openrig install>/dist/builtins/workflow-specs/ and adapt.
Once written:
rig workflow validate <workspace_root>/specs/workflows/<name>.yaml --json
rig workflow instantiate <workspace_root>/specs/workflows/<name>.yaml \
--root-objective "<one-line objective for the run>" \
--created-by <your-session>@<your-rig> \
--json
Both --root-objective <text> and --created-by <session> are REQUIRED
on instantiate — omitting either yields a Commander required-option
error before the daemon is contacted. --entry-owner <session> is an
optional override for the entry-step owner; default routing is per the
workflow spec.
validate returns a structured ok/error report; instantiate creates a
workflow instance + entry-step qitem. Inspect existing surface state with:
rig workflow specs --json # list registered specs (built-in + operator-authored)
rig workflow list --json # list active workflow instances
rig workflow show <instanceId> --json # inspect one instance
rig workflow project <instanceId> # ADVANCE an instance — projects the next-step packet
rig workflow continue <instanceId> # read-only inspector of an instance (does NOT advance it)
(Surface note — the current rig workflow command group registers 13 subcommands: validate, instantiate, project, list, specs, show, trace, continue, run, watch, route, resume, status. There is still no create verb — the spec YAML is authored on disk. project is the advancing verb (it projects the next-step packet); continue is a read-only inspector, NOT an advance — do not conflate them. The 13-verb set and the project-vs-continue semantics are verified against current product main d37a08ad (packages/cli/src/commands/workflow.ts, 13 registered .command(...) entries; the earlier "6-verb surface / continue-advances" claim here was stale). Verify individual subcommand flags with rig workflow --help.)
Permission policy — pick one at setup (onboarding)
OpenRig sets only a minimal usability floor on your harness permissions and otherwise stays out of the way — then it ships recommended policies you opt into. It never bakes a permission policy for you. On install / onboarding this is a required choice, presented as a top-level pick:
-
POLICY MODE — pick a built-in policy and have it applied:
- Locked — deny-by-default whitelist; untrusted rigs/work.
- Standard ⭐ (recommended) — routine development including push is allowed; PR creation, publication, merge/release, force-push and destructive actions ask.
- Open — allow-by-default; everything except explicitly-destructive, which ask.
The built-in definitions ship as read-only policy spec files (Locked / Standard / Open); applying your pick is the job of the
applying-a-permission-policyskill — it translates the chosen spec into your live harness config (Claudesettings.json/ Codexconfig.toml), interactively, showing the diff before it writes. -
YOLO MODE — done with permissions, just want it to work: OpenRig boots every seat with the harness full-bypass launch flag. No config policy is applied (the bypass overrides it). This is a deterministic OpenRig setting, not a skill.
-
No choice = the floor — the minimal usability baseline (Claude
acceptEdits/ Codex workspace-only / Pi--no-approve), one consistent minimum, nothing more.
The floor and YOLO are launch flags OpenRig sets deterministically; the Locked / Standard / Open policies are config-file policies the skill applies (agent-driven, because harness config formats drift). A rig carries its chosen policy on its spec and boots with it — see Lifecycle → Bring a rig up. To (re)apply or change a policy, open applying-a-permission-policy.
v0.3.x Starter, Workspace, And Plugin Surfaces
OpenRig v0.3.0 adds rig agent-image, rig context-pack, rig workspace, and
rig config init-workspace. (0.5.0: the rig context-pack alias is retired — the store + compose library is the single rig context noun; see "Context packs and paced delivery (0.5.0)".) It also shifts fresh-user starter guidance toward
product-team for human-directed work and conveyor for workflow-oriented
work. Treat demo as legacy/test content unless a task specifically asks for
the old demo spec. (0.6.6: the built-in teams are starter and factory, plus the
specialist teams code-review, research and pm. product-team, conveyor and
demo are gone, and first-project is starter's old name. workshop is a rig
bundle.)
OpenRig v0.3.1 adds public package/source surfaces for Plugin Primitive v0,
Claude Auto-Compaction Policy, migration 040_workflow_specs_diagnostic,
Library Explorer finishing, Settings Destination Explorer, Dashboard/For You
vellum refresh, storytelling adapter, and action outcome + inline error UX.
rig plugin is read-only at v0:
rig plugin list
rig plugin show <id>
rig plugin used-by <id>
rig plugin validate <path>
There is no rig plugin install verb in v0.3.1. Plugin installation remains
explicit operator copy/symlink to $OPENRIG_HOME/plugins/<plugin-id>/.
The v0.3.1 package introduced opt-in Claude auto-compaction policy through
policies.claude_compaction.* ConfigStore keys. A package version alone says
nothing about a running daemon's configuration; inspect the selected instance
before relying on a policy or its default.
Compatibility checks:
rig downaccepts a rig name or id. An ambiguous name matching more than one active rig is refused with matching ids; use the intended id. It ends every agent's session and any work in progress, so checkrig ps --nodes --rig <rig>and that the person asked;rig up <rig> --existingrestores it.- For queue/view JSON or limit differences, compare the installed command's help, the running daemon version and the actual response. A wrapper mismatch is not by itself a daemon-health failure, and historical workarounds are not current behavior guarantees.
- After a startup timeout, inspect status and logs before retrying; a timeout does not establish whether the underlying operation completed.
Recovery and Resilience (v0.3.4+)
v0.3.4's theme is Recovery + Resilience. The surfaces below compose into a single boot-to-running-rig path that survives crashes, hand-resumed sessions, profile-load drift, and partial workspace state without silently fudging status.
rig start — recovery entrypoint
rig start is the top-level recovery sequencer. It does not invent recovery;
it composes existing primitives (daemon start + kernel verify + per-rig
restore) into one call.
rig start # interactive: daemon + kernel + pick-and-restore
rig start --last # headless: restore rigs that were last running; a rig stopped on purpose stays down
rig start --all # headless: restore all rigs with restore-usable snapshots
rig start --rigs <name> [<name>...] # headless: restore only the named rigs
rig start --json # JSON output for agents
rig start --last keeps down a rig that was stopped on purpose (rig down or rig seat stop).
Bring it back with rig start --rigs <rig>. A rig whose seat statuses can't be read is still
attempted, with a warning (seat_status_unreadable).
Framing: rig start is the RECOVERY entry point, not the getting-started
hero. The fresh-user boot hero remains rig up <team> (typically
rig up starter for a first change, or rig up factory). Reach for rig start after a host reboot, daemon
restart, or any "bring my rigs back" moment.
rig reconcile-session — no-launch adopt of a hand-resumed session
When an operator has externally resumed an agent session (e.g. attached a shell, restarted a runtime by hand) and you want OpenRig to reconcile its lifecycle state without re-launching or sending input, use:
rig reconcile-session <session>
rig reconcile-session <session> --rig <rigId> --node <logicalId>
rig reconcile-session <session> --no-launch
rig reconcile-session <session> --json
rig reconcile-session <session> --repair-missing-pane --no-launch
This is a no-launch, no-input adopt: it records a new claimed session row for
the seat (any running row is superseded); it re-captures the resume token if
it can, and conversation continuity stays unverified. --rig/--node
disambiguate when the canonical session name does not uniquely resolve.
--no-launch is accepted for explicitness.
--repair-missing-pane --no-launch is different: for a running launched seat
whose tmux pane was never recorded, it fills in the pane and keeps the
session, resume token and generation. It never adopts. Both forms refuse
while the seat's typing protection is on.
Five-term restore status vocabulary
The shipped restore vocabulary is intentionally honest. It surfaces in
rig up / rig restore / rig ps. Use the term that fits — do not collapse
to a generic "ok/failed":
resumed— seat resumed from its original session/snapshot and is live.fresh-primed— seat opted into--freshand was freshly started.awaiting-decision— zero-session honest state. There is no resumable session AND no--freshopt-in was given; the seat is waiting for an operator decision. Previously fudged asfailed; that was wrong — nothing is broken, the system is asking for input.attention_required— seat is in a state needing operator attention; not a transport failure. Clear viarig seat clear-attentiononce the attention has been resolved.failed— the send transport or launch genuinely failed.
This replaces the prior collapsed model (the v0.3.3 four-term vocabulary, in
which rebuilt was a term, is retired).
rig seat clear-attention — audited reconcile of stuck attention
When a seat is stuck in attention_required, do NOT hand-edit SQLite to
fake-clear the state. Use the evidence-gated, operator-attested, audited
reconcile:
rig seat clear-attention <session>
rig seat clear-attention <session> --reason "operator attested: the operator re-authed, confirmed live"
rig seat clear-attention <session> --json
--reason <text> can acknowledge startup-status and subset-restore attention.
It does not bypass full-restore continuity checks or an active pane-identity
mismatch/missing-pane check; those run first. Successful identity re-verification
can also clear coexisting startup or subset-restore attention. If neither earlier
path clears attention, omitting --reason requires activity or send evidence.
Startup and restore clears write their corresponding audit events; an identity-only
clear updates the current binding and identity verdict without a clear event.
Acknowledgment or responsiveness alone does not prove that the original
conversation resumed.
Two startup cases also end in attention_required, and the result says what to do:
- Assisted startup (a community harness preset with
startup_assisted: true): the startup text and Enter were sent but submission isn't confirmed. Check the pane and submit any staged text, don't resend it, then run the reportedrig seat clear-attention <session> --reason "..."once the seat has responded. - A refused startup turn (Claude answered a startup turn with a refusal such as an expired login): nothing more is
typed into the seat, and the result names how many later startup inputs weren't sent. A fresh start that ran no
one-shot action stays continuable:
rig seat continue <seat>delivers the whole startup again, the refused turn included. A resumed or forked seat keeps its conversation and isn't offered continue; a fresh start that can't continue namesrig seat launch <seat> --fresh --stop --reason "<why>".
A 422 response names the uncleared class and its failed check. If the recorded
native token needs correction and you know the actual token, use
rig seat set-resume-token <session> --token-stdin --reason <explanation>, then
rerun rig seat clear-attention <session> to check the live evidence. The token
update records operator provenance; it does not itself prove continuity.
Stopping/relaunching a working seat is a separate disruptive operation, not a
required cleanup or proof of resumed lineage.
Periodic snapshots — crash-insurance floor
The daemon ships a periodic-snapshot scheduler. It runs independently of teardown events and provides the crash-insurance floor that prior event-only/teardown-only snapshots could not provide on hard crashes.
Config keys (SettingsStore):
snapshots.periodic.enabled— defaulttruesnapshots.periodic.interval_seconds— default300snapshots.periodic.retention_keep— default10
Newest-wins semantics: when both auto-periodic and auto-pre-down
snapshots exist for a rig, the freshest of the two is selected for restore.
A newer auto-periodic beats a stale auto-pre-down (the crash fix); a
genuinely-fresher auto-pre-down still wins on graceful cycles. Manual
snapshots are handled separately. See
packages/daemon/src/domain/snapshot-repository.ts for the ordering rule.
The last-snapshot floor surfaces in rig ps / status output so an operator
can see at a glance how recent the crash-insurance floor is.
Codex profile-v2 preflight
Profile-bearing launch/restore surfaces run a profile-load preflight. When profile-load issues are detected, the failure is honest and actionable (named error + remediation pointer) instead of a silent partial launch that would later look like an attention_required seat with no explanation.
cmux launch readiness
cmux-backed launches no longer produce silent partial workspace state. When parts of the workspace are missing, the launch surfaces partial state honestly and the UI exposes a one-click open-missing affordance.
(See also ## Token-Efficient Defaults (v0.4.0+) below for the compact-by-default read-command surface that lands in 0.4.0.)
Token-Efficient Defaults (v0.4.0+)
v0.4.0 flips the five most frequently invoked read-commands from firehose-by-default to compact-by-default, and rig queue list adopts the docker / kubectl read-command grammar. All defaults preserve breadth and capability — the firehose is one explicit flag away.
rig ps — scope-aware: bare rig ps = ALL rigs; --nodes = your rig only
rig ps # ALL active rigs on the host, one compact row each — RUN FIRST to know the world
rig ps --rig <name> # one named rig's summary
rig ps --nodes --rig <name> # per-node (seat) detail for a NAMED rig — the normal drill-in
rig ps --nodes # per-node detail — CURRENT rig ONLY (deliberately narrow; NOT the whole host)
rig ps --json # compact JSON (default = a bare array of ALL non-archived rigs)
rig ps --nodes -A # cross-rig node inventory (was v0.3.4 default)
rig ps --nodes --full # complete record (the v0.3.4 per-node default shape; resumeToken VALUE retained here for downstream consumers)
rig ps --nodes --session <sess> # narrow to one canonical session
rig ps --active # opt-in active-state filter (does NOT change the all-states default — ps surfaces topology/readiness, where stopped/recoverable/attention IS the actionable signal)
v0.4.0 breadth + projection changes:
- Rig-level
rig pslists ALL active rigs (one row each — the cheap "know the world" view). The--nodes(per-seat) view defaults to your CURRENT rig only (fromOPENRIG_SESSION_NAME's@<rig>suffix);--rig <name>picks another rig,-Awidens--nodesto the whole host (expensive — prefer--fields/--limit). - Per-node TL;DR projection (compact) is the default;
--fullreturns the raw byte-equivalent passthrough. Daemon-siderecoveryGuidancerelocated to a guidance-by-reference map (no longer duplicated per-node) — even--fullbenefits. - All-states stays default (different from
rig queue listwhich defaults to active-only) — forps, non-running states ARE often the actionable signal. - Resume-token security:
--fullJSON emitsresumeTokenPresent(boolean) — the actualresumeTokenvalue also remains in--fullfor downstream consumers that legitimately need it, but the compact default never carries it (an orch glance never accidentally leaks token material).
⚠ SCOPE-AWARENESS — the one that bites: rig ps --nodes (and --nodes --json) show ONLY your current rig's seats, by design — the narrow default protects your context window. Narrow output is not the whole world. Never conclude "my rig is the only rig on the host" from a --nodes read — run bare rig ps FIRST (cheap; it lists every rig), then rig ps --nodes --rig <name> for the one you need. (-A widens to the whole-host node view; choose it when that breadth is needed.)
rig whoami — compact-by-default + --full (--verbose alias)
rig whoami # compact: identity + peers names + edges + transcript path
rig whoami --json # compact JSON
rig whoami --json --all-peers # compact JSON with every peer, when output.compact bounds the list
rig whoami --full # complete payload (v0.3.4 default shape)
rig whoami --verbose # alias of --full
The first command every agent runs on boot AND every compaction-restore. The compact default keeps identity-recovery essentials (identity, peers names + sessionNames, peersNote, edges directional kind + to.sessionName, the transcript path). --full adds contextUsage, commands, runtimeContext and workspace. With output.compact on, compact JSON lists at most output.whoami_peers peers (default 8): edge peers first, then your pod, then roster order. peersNote and peersOmitted then say how many were left out, and --all-peers lists every one; edges stays complete. The compact-default is an ALLOWLIST projection — future payload fields default to --full and cannot silently re-bloat the every-boot path.
rig queue list — active-frontier + docker/kubectl grammar
rig queue list # active, compact, CURRENT-rig (docker-ps default)
rig queue list -a # + closed/done history within current breadth (docker -a)
rig queue list -A # cross-rig breadth (kubectl -A)
rig queue list --full # add body + chain-of-record + transition history
rig queue list -o json # compact JSON (token-safe, machine-parseable)
rig queue list --full -o json # full JSON
rig queue list --mine # just the caller's items
rig queue list --destination <s> # destined to <s>
rig queue list --source <s> # sourced by <s>
rig queue show <qitemId> # bounded single-item body preview
rig queue show <qitemId> --full # complete body and chain fields; a closed row adds closingTransition (the close's state, reason, target, actor, time and note)
Four orthogonal axes (scope × history × field-breadth × encoding), all composable. STOP using bare rig queue list as the cross-rig firehose. Default is now active + compact + current-rig. Cross-rig history with full bodies is opt-in via -A -a --full; request only the breadth and fields needed for the question.
rig restore-check — summary + not-ready-only default + --full
rig restore-check # summary counts + not-ready seats (with reasons) only
rig restore-check --full # complete per-seat readiness across the fleet (v0.3.4 default)
rig restore-check --rig <name> # narrow
rig restore-check --as <session> # narrow to one seat
The summary retains not-ready seats and their reasons; --full adds ready-seat detail when needed. Scope the query before expanding its payload.
rig context — context-window usage viewer (0.4.x; REMOVED in 0.5.0)
rig context # compact summary (0.4.x only)
rig context --full # complete current payload
rig context --rig <name> # narrow to one rig
rig context --threshold 80 # filter to seats at/above 80%
Lower leverage than the others; keeps the read-command surface compact-by-default after the 0.4.0 upgrade. ⚠ 0.5.0: this usage viewer is removed entirely and the rig context name is reassigned to the context library (store + compose) — see "Context packs and paced delivery (0.5.0)" below. On a 0.5.0 host, bare rig context is the library, not this viewer.
Keep routine reads bounded
Choose scope, active/history breadth and fields before expanding a result. A status question usually needs identifiers, owner, state and reason; open the full body or artifact when it is relevant. Preserve full evidence on disk instead of repeatedly loading unchanged output. Compact defaults reduce reading cost; they do not remove the full-detail path or prove that nothing exists outside the scope.
rig scope mission|slice progress — deterministic progress updates
rig scope mission progress <mission> --add "<line>" # append a progress line; --set replaces; --section <heading> (default Rail); --status active|done|blocked
rig scope slice progress <slice-path> --add "<line>" # same flags: --add / --set, --section <heading>, --status active|done|blocked
Replaces hand-editing PROGRESS.md with markdown. Writes the canonical structure the OpenRig PROGRESS UI page reads. rig scope mission create + rig scope slice create now scaffold PROGRESS.md automatically.
rig scope mission|slice stage / verified / repair — deterministic maturity vocabulary
rig scope slice stage <slice> <new-stage> # wip / provisional / established / canonical / superseded / retired
rig scope slice stage <slice> superseded --successor <id> # superseded REQUIRES --successor (rejected otherwise)
rig scope mission stage <mission> <new-stage> # same enum + rules at mission tier
rig scope slice verified <slice> --against "<source>" # stamp `verified: <today> against <source>`; --against MANDATORY
rig scope mission verified <mission> --against "<source>"
rig scope slice repair <slice> # idempotent repair: backfill PROGRESS.md, conform id/stage/verified, repair ghosts
rig scope mission repair <mission> # mission-tier idempotent repair
rig scope slice show <slice> # derives read-time effective-reliability from (stage × verified)
# — stale-`verified` `canonical` reported as effectively `provisional`
Composes with the progress command + scaffolding to update scope IDs and maturity vocabulary through rig scope. Agents update stage / verified / id through commands rather than hand-editing markdown and drifting. The --against MANDATORY rule on verified is the anti-stale keystone: bare timestamps are rejected because a bare timestamp is exactly what lets stale trackers lie while looking fresh. STOP hand-editing the stage / verified / id fields in scope frontmatter; use the new verbs. Existing missions / slices with id:null ghosts or missing PROGRESS.md are repaired idempotently via repair.
rig skill audit — skill cascade provenance
rig skill audit # human report of findings
rig skill audit --json # structured findings
rig skill audit --severity warn # stale + mirror-drift only
rig skill audit --rig <name> # narrow to embedded skill copies for one rig
Read-only audit of the skill cascade. Detects missing / stale / self-referential / invalid-date / mirror-drift across the canonical skills workspace → product mirror → hub cwd → installed plugin chain. Findings route back to the lifecycle for shaped propagation runs. False-green prevention: when audit evidence is unavailable, the CLI emits unable-to-audit with exit code 2 rather than reporting clean.
rig seat clear-attention — extended to derived projection staleness
clear-attention also reaches restore-derived attention even when
startupStatus=ready and sessionStatus=running. Full-restore attention requires
the restore reconciler's exact native-token and usable-pane checks; --reason
cannot replace them. Subset-restore attention can reach the attestation path
when no active pane-identity class takes precedence. That path records
operator_recovered with runtimeCwdVerified:false; it is an acknowledgment,
not proof of resumed lineage. Without an explicit stored continuity outcome,
inventory leaves continuity null (unverified in rig seat status) unless the
same restore attempt has a matching receipt and reconciliation with strict
native-token and usable-pane proof. Explicit stored outcomes remain historical
facts; acknowledgment or responsiveness alone cannot infer resumed.
Native Codex session id capture
Codex seats can now record the real native session id from the Codex
SessionStart hook instead of relying on scrape-shaped identity. Corroborate the id across the current hook payload, provider history and
managed record before relying on it. A release introducing native capture does
not prove that every existing seat uses it; retain any unavailable or conflicting
identity evidence explicitly.
Codex resume preserves approval posture
Resuming a Codex seat preserves the launching seat's approval/sandbox posture and profile flags. Product-emitted resume commands carry the posture flags instead of silently falling back to implicit-deny or an unrelated profile.
Do not "fix" a resumed Codex seat by relaunching it with broader approvals unless the operator explicitly grants a bounded window. Verify the seat's active posture first, and preserve it when composing recovery commands.
rig seat set-resume-token --token-stdin
printf '%s' "$RESUME_TOKEN" | rig seat set-resume-token <session> --token-stdin
Use this command to set or restore a seat resume token. It replaces direct SQLite edits, rejects unauthorized writes and bad/null token false-ready paths, records redacted audit/provenance, and keeps token material out of command arguments, stdout, logs, and normal status rows. Use stdin, not an inline flag, when passing token material.
Core Loop
Most work in OpenRig reduces to this loop:
- recover identity:
rig whoami(compact default; add--fullonly when you need the heavy payload) - inspect inventory:
rig ps --nodes(compact default; add--fullonly when you need the firehose) - read context:
rig transcript ...,rig ask ...,rig chatroom history ... - act:
rig send,rig capture,rig broadcast, lifecycle commands
Agent-Managed Apps
An agent-managed app is a deployable OpenRig unit made of:
- the software or service
- one specialist agent dedicated to that software
Treat the specialist as the domain delegate for that app. The current canonical example is:
- rig:
secrets-manager - pod:
vault - member:
specialist - logical ID:
vault.specialist - session:
vault-specialist@secrets-manager
Typical operator loop:
rig up secrets-manager --cwd /path/to/project
rig ps --nodes --json
rig send vault-specialist@secrets-manager "Check Vault health and report back." --verify
rig env status secrets-manager
rig env logs secrets-manager
Cross-rig communication is valid when the target session resolves uniquely. Example:
rig send vault-specialist@secrets-manager "Read secret/data/dogfood and report the value." --verify
Use the specialist instead of teaching every peer the same app-specific toolchain.
For Vault, ask vault.specialist to do secrets-domain work rather than improvising curl or Vault CLI usage in unrelated agents.
Identity and Recovery
Start here after launch, compaction, or confusion:
rig whoami --json
What it gives you today:
- identity: rig, logical ID, pod/member, session name, runtime
- peers and directional edges
- transcript info
contextUsagewhen available
Flags:
rig whoami --session <name>
rig whoami --node-id <id>
If the daemon is unreachable but identity can still be inferred, --json may return a partial result instead of crashing.
WhoamiResult (v0.3.3+) carries a required peersNote field with three pointers
the agent can use to navigate the rest of the rig from a cold start. The
human-formatted CLI output preserves the literal Peers: line prefix verbatim
(parser/test compatibility) and surfaces the clarifier in-band beneath it; the
JSON form exposes peersNote directly for programmatic consumers.
Inventory and Monitoring
rig ps # ALL active rigs on the host, one compact row each (run FIRST to know the world)
rig ps --nodes # compact node inventory (current rig)
rig ps -A # all-rigs breadth (was the pre-0.4.0 default)
rig ps --nodes --full # complete per-node record (the firehose — opt-in)
rig ps --nodes --json # compact JSON node inventory (add --full for the full record)
v0.4.0 flipped these to compact-by-default — see the rig ps compact-defaults section above; STOP using bare rig ps --nodes --json as a fleet-wide firehose (scope and detail are separate choices). The compact rig ps --nodes node inventory (add --full only when you need the complete record, -A for cross-rig breadth) carries, per node:
- session name
- runtime
- session/startup status
- restore outcome (compact:
resumeTokenPresentboolean; the token VALUE is in--full) - attach/resume commands
- latest error
Other health surfaces:
rig status
rig daemon status
rig config
rig preflight
rig doctor
rig env status <rig>
rig env logs <rig>
rig env down <rig>
Bounded agent self-scout
Use the typed health projection before reading raw coordination history. The default query is the current seat; widen deliberately when the evidence points beyond it:
rig health --json
rig health --rig <rig-id> --json
rig health --instance --json
rig health explain <finding-id> --json
Follow the returned stable finding ID and suggestedInspection. Use explain
when the summary matters: it returns the same canonical record with its bounded
window, freshness, literal detector rule, evidence references, and next
inspection. Human output projects those same fields; it does not calculate a
second score.
An empty result means only that no records matched the bounded query. It is
not a healthy assertion. Stale, unavailable, contradictory, and
indeterminate evidence stays explicit. Never read raw SQLite for a self-scout,
and never turn a finding into an acknowledgement, notification, queue row, or
remediation automatically: rig health is strictly read-only.
Transcript and Communication
Transcript access
rig transcript <session> --tail 100
rig transcript <session> --grep "pattern"
rig transcript <session> --json
Send to one session
rig send <session> "message"
rig send <session> "message" --verify
rig send <session> "message" --wait-for-idle <seconds>
rig send <session> "message" --raw
rig send <session> "message" --dangerously-interact --reason "<why>"
rig send <session> "message" --host <id>
rig send <session> "message" --json
The send-guard (v0.4.0) — the default is SAFE. A default rig send is guarded: it will NOT submit into an interactive prompt / permission block on the target pane. That includes Claude's MCP server approval menu (N new MCP servers found in .mcp.json, ending Space to select · Enter to confirm · Esc to reject all) while it is the current screen: the send is refused with reason target_needs_input and Refused: '<session>' is at an interactive prompt (selection_prompt). A message must not select or approve it. … No text was sent. Approving or rejecting those servers is the person's call; --dangerously-interact drives it only deliberately. Flags:
--verify— delivery evidence.--force— a back-compat no-op on the send DECISION: it never bypasses the interactive-prompt/permission guard and never changes whether a message is delivered (a mid-task/busy pane already sends-with-advisory by default). (It does NOT "bypass activity-risk checks" — that earlier teaching is retired.) It is not fully inert, though — it is still parsed solely to be rejected in combination with--wait-for-idle:rig send … --force --wait-for-idle <n>prints--wait-for-idle cannot be combined with --force, exits 1, and sends nothing. So do not read "no-op" as "--force --wait-for-idleis harmless"; that pairing errors. (Verified against current product maind37a08ad: the guard-bypass no-op is declared atsend.tsand confirmed by runtime capture — a plain--forcesend delivers through the ordinary path; the--wait-for-idlerejection is enforced atsend.ts,routes/transport.ts, andsession-transport.ts, and confirmed by runtime capture — exit 1, nothing sent.)--wait-for-idle <seconds>— wait until the target is explicitly idle before sending. Cannot be combined with--force(that pairing is rejected: exit 1, nothing sent).--raw— send exact text/keystrokes without the From/To messaging envelope (still guarded against interactive prompts).--dangerously-interact --reason "<why>"— the ONLY override of the prompt/permission guard: deliberately drive an interactive prompt/permission block (implies--raw, requires--reason, audit-logged). On a guard-attached managed seat, a non-menu override stops and shows existing typed/pasted input (author unknown) withoverride_input_present; unreadable input stops withoverride_input_unknown. Recognized dim suggestions are shown separately and count as empty; a collapsed paste shows only its label, not hidden contents. Inspect the input, deliberately clear the native prompt with native/tmux controls, then retry. Manual clearing is outside OpenRig's lease and observation checks and is not recorded; human typing can still race a read and paste. When you deliberately need to type without that composer check (for example an unsupported input you inspected yourself), add--type-without-checkingwith--dangerously-interact --reason "<why>". It does not clear input and may join or submit existing text. Ordinary sends and wakes keep their current rules.--host <id>— send on a remote host declared in~/.openrig/hosts.yaml(ssh hosts shell out; http hosts go CLI-direct to the remote daemon).--from <session>— deprecated and ignored; it does not select the sender. Sender identity comes from the current seat and transport provenance; cross-host envelopes use the durable local origin, not the supplied flag or a relay identity.--context <ref>(0.5.0) — attach a composed context pack/piece by ref (see "Context packs and paced delivery"). Small piece →send --context; a real pack →rig walk. The nounrig contextcomposes the ref; the verb delivers it.
Durable work goes to the QUEUE, not
send.rig sendis an ephemeral message to a pane — it can be missed, and its delivery status is pane-render, not receipt. If you are assigning work, or the message is important enough that losing it would be a real bummer, userig queue(below): it's durable, owned, tracked, and survives compaction and restart. Reach forsendfor a quick conversational nudge; reach for the queue for anything that must not get lost. Do not default tosendfor work — that's the most common mistake.
As of v0.3.3, content beginning with -- or - is safe:
rig send <session> "content starting with -- or - is now safe" delivers
literally. The daemon's send_text path carries an explicit --
end-of-options sentinel so tmux no longer parses dash-prefixed content
as its own flags. The CLI surface itself is unchanged. For multi-line
or large bodies handed off as durable work, use
rig queue create --body-file <path> (- for stdin) — that's the
queue-side surface, not rig send.
--verify delivery outcomes (v0.3.3+):
delivered— text + Enter both succeeded and capture re-confirmed the body landed.rendered-unconfirmed— text + Enter both succeeded but capture could not re-confirm the body (TUI redraw race or scroll). The message landed; the post-send re-check could not prove it. Treat as landed-but-unconfirmable, NOT failure.failed— the send transport itself failed.
The legacy Verified: yes/no line is preserved verbatim (parser/test
compatibility). A new Delivery: <outcome> line carries the named outcome
above.
Observed operator nuance for --verify:
Sent to ...+Verified: yes(Delivery: delivered) = strong positive delivery evidence.Sent to ...+Verified: no+Delivery: rendered-unconfirmed= the message landed; capture could not re-prove it. Don't blind-retry — check reply /rig capture/ transcript before sending again.Sent to ...+Verified: no+Delivery: failed= send-transport failure.- no
Sent to ...line or a hard error = send failure.
When you get Verified: no, do not immediately retry blindly. First check one of:
- a direct reply from the target
rig capture <session>- transcript evidence
- queue/outbox state if the message asked for a durable handoff
Capture terminal output
rig capture <session>
rig capture <session> --lines 50
rig capture --rig <name>
rig capture --pod <name> --rig <name>
rig capture --rig <name> --json
Broadcast
rig broadcast --rig <name> "message"
rig broadcast --pod <name> "message"
rig broadcast "message"
rig broadcast --rig <name> "message" --json
Use rig broadcast sparingly — prefer rig send fan-out. Without --rig or --pod, broadcast targets
every running session across ALL rigs (plus attached external_cli nodes) — the fastest way to cause a
broadcast storm. Reserve rig broadcast for small rigs or a genuine all-hands emergency. For the normal
"message several seats at once" case, use rig send, which scopes the fan-out and keeps the messaging
envelope, the delivery/interactive-prompt guards, and per-recipient results:
rig send --to dev-impl@my-rig,dev-qa@my-rig "message to specific seats" # named seats (comma-list or repeat --to)
rig send --pod dev "message to one pod" # scoped fan-out
rig send --rig my-rig "message to one rig" # scoped fan-out
Chatroom
rig chatroom send <rig> <message> [--sender <name>]
rig chatroom history <rig> [--topic <name>] [--after <id>] [--since <ts>] [--sender <name>] [--limit <n>] [--json]
rig chatroom wait <rig> [--after <id>] [--topic <name>] [--sender <name>] [--timeout <seconds>] [--json]
rig chatroom clear <rig>
rig chatroom topic <rig> <topic-name> [--body <text>] [--sender <name>]
rig chatroom watch <rig> [--tmux]
Key commands:
send— post a messagehistory— retrieve with composable filters (sender, since, after, topic)wait— block until new matching messages arrive (polls history, times out honestly)clear— delete all messages for the rig (destructive, rig-scoped)
See something, say something
OpenRig has an observation stream — the fleet's zero-friction institutional memory, mined for real product improvements. When you notice anything worth externalizing, say something and keep working:
- a bug, a rough edge, or something that needs fixing
- a feature idea or an improvement
- something that worked really well — a technique, tool, or pattern worth spreading
- an observation, positive or negative feedback, or something genuinely cool, productive, or funny
rig stream emit --source <your-session> --body "what you noticed"
That's the whole reflex. Don't decide where it goes or who it's for — the intake router triages
(destination/type/urgency/tags are optional hints — --hint-type review|handoff|idea,
--hint-urgency routine|urgent|critical, --hint-tags — never required). One command, then carry
on; the value is the habit, not the polish. Don't overdo it, either: stream real signal, not
narration — a good observation beats ten noisy ones. It's a passing thought you externalize, not a chore.
topic— set a topic markerwatch— SSE or tmux-based live stream
Roundtable protocol:
- Inspect old room:
rig chatroom history my-rig --limit 5 - Save if needed:
rig chatroom history my-rig --json > /tmp/old-room.json - Clear if needed:
rig chatroom clear my-rig - Set topic:
rig chatroom topic my-rig "ROUND START" - Post:
rig chatroom send my-rig "position..." --sender <session> - Monitor:
rig chatroom wait my-rig --timeout 120 - Close:
rig chatroom topic my-rig "ROUND CLOSED"
rig ask
rig ask <rig> "question"
rig ask <rig> "question" --json
Current shipped behavior:
- queries the daemon for evidence
- returns rig summary
- returns transcript excerpts
- may return chat excerpts
- returns insufficiency state and optional guidance
This is an evidence/context command. It is not a hidden second-LLM call.
rig auth — agent auth-profile management (v0.4.1, product-native)
Product-native switching of agent auth profiles from the CLI. The runtime is a flag (--runtime <codex>), not a command noun — never rig codex-auth.
rig auth status --runtime codex # presence / mode / parseability / login-state (never prints token contents)
rig auth list --runtime codex # saved profiles
rig auth save <profile> --runtime codex # snapshot the auth FILE (mode-guarded), never echoes contents
rig auth switch <profile> --runtime codex
rig auth validate <profile> --runtime codex
rig auth seats … --runtime codex # seat -> profile registry (metadata only; NOT proof of a live account)
Hard secret boundary: no token value is ever printed, logged, queued, streamed, or committed; status/validate report presence/mode/login-state only; seat labels are metadata, not live-account proof. MVP is --runtime codex; other runtimes use the same surface with a different --runtime, never a parallel command.
Give a seat its own Claude or Codex account
A runtime's login lives in its home: Claude Code's in its config dir (CLAUDE_CONFIG_DIR, default ~/.claude), Codex's
in CODEX_HOME (default ~/.codex). A seat that names its own home in env: runs on that home's login.
- Prepare the home once, natively, in a terminal:
CLAUDE_CONFIG_DIR=/abs/path claude, then/login; orCODEX_HOME=/abs/path codex login. The person signs in; never copy credential files between homes. - Name it in the member's
env:inrig.yaml, not withrig up --env, which leaves these two out (CLAUDE_CONFIG_DIR: /abs/pathorCODEX_HOME: /abs/path), or in the rig'senv:for every seat of that runtime. The member's value wins. It must be an absolute path: any other value is reported and ignored, and the seat keeps the daemon's home. - It takes effect at the seat's next launch (a fresh launch, restore or handover); a running seat keeps the home it started with. A conversation saved under the old home doesn't resume from the new one.
- Check it:
rig seat status <seat>shows the home and where it came from, andrig provider statuslists it as the seat's configured account. Both say what's configured, not which login a running session holds.
Seats may share a home. OpenRig writes its own settings (Codex hooks, managed config.toml and plugin; Claude's
first-run trust settings) into the seat's home.
Move one Codex seat to another login
For a Codex seat on Linux. The login must already be saved in the Codex home the seat runs on (rig seat status <seat>
shows it). Other operating systems, and moving a seat to another home, aren't supported by this command yet: say so instead
of improvising. A Claude seat moves differently; see the next section.
- Save each login once, in that home:
CODEX_HOME=<home> codex login(the person signs in), thenCODEX_HOME=<home> rig auth save <name>.rig auth list --codex-home <home>lists them. - Check:
rig provider precheck --seat <seat> --to-account <name>shows the home, what the seat is doing, and every peer on that home. A refusal says what's missing, and nothing changes. - Move:
rig provider switch --seat <seat> --to-account <name>. It saves the home's current login asbefore-switch-<time>, activates the chosen one, and restarts the seat in place on the same conversation. If the current login can't be saved first, nothing changes. Peers on the home aren't restarted. Each keeps its current login only until its next token refresh, when Codex stops it with "signed in to another account". Run the printedrig seat restart <peer> --keep-conversation --reason "..."for each to move it too. Don't sign in again in that home: that replaces the login you just switched to. - Confirm: after the seat completes a real tool-using turn, run the printed
rig seat restart <seat> --keep-conversation --verify --expect-account <its Account line>. Until then the move is unconfirmed. An uncertain restart is reported; never restart it a second time.
Move one Claude seat to its own subscription token
The seat keeps its config dir and gets its own CLAUDE_CODE_OAUTH_TOKEN, read from a file as Claude starts. OpenRig keeps only
the file's path and your label, never the token.
- Prepare the token once: the person runs
claude setup-token, which prints a one-year subscription token, and saves it to a file only their user can read (chmod 600). Never paste the token into a message, a row or a command line. - Check:
rig provider precheck --seat <seat> --to-account <label> --token-file <absolute path>. It refuses, changing nothing, if the file wouldn't apply, the seat isn't running, or something Claude Code ranks above the token is set (a cloud provider,ANTHROPIC_AUTH_TOKEN,ANTHROPIC_API_KEY, anapiKeyHelper). It names each one and how to remove it for that seat. When the token applies, the launch unsets those names for Claude's process, so the pane shell's own exports can't outrank it. Managed settings can't be read; the seat's/statusshows what it actually uses. - Move:
rig provider switch --seat <seat> --to-account <label> --token-file <absolute path>. It takes a snapshot that resumes the seat's conversation, sets the reference, and relaunches the seat from that snapshot in its own pane: the pane and its scrollback stay, but work in flight when Claude ended doesn't carry over. Seats sharing the config dir keep their own login.--stored-loginmoves the seat back; if the seat still receives aCLAUDE_CODE_OAUTH_TOKEN, Claude uses that instead of the stored login, and the report names it. - Confirm: after the seat completes a real tool-using turn, its
/statusshows token auth, with no account email. The label is yours, not an account Claude observed: say so. A token session can't use claude.ai connectors or Remote Control. The token is in Claude's environment, so the tools, hooks and MCP servers it starts inherit it; don't have it print its environment. A relaunch that didn't resume reads unknown, with its recovery command; never run the switch again for it.rig seat status <seat>shows the reference, and says when its file won't apply at the next launch.
Manage subscriptions across a fleet (when the person asks you to)
This applies only when the person has asked you to manage their subscriptions. Otherwise, don't take this job on. Installing or upgrading OpenRig appoints no agent, launches no bundle and starts no rotation.
When a person runs several Claude and Codex subscriptions across machines, the fleet studio bundle's one agent is meant to keep work running, through the Fleet app. Until that bundle is available, the agent the person asks to manage their subscriptions can use this section. If that's you, you decide which subscription or home moves, and when. You decide from:
- the usage and assignment readings, and their freshness;
- your earlier move reports (
rig provider moves); - the person's account policy, or their standing preferences when there's no policy.
You say why, and you see the outcome through. OpenRig gives you the facts and the tools; it never decides for you, and there's no threshold to obey.
Who decides:
- The person's operator agent can ask you for a move, or ask what you've moved. It doesn't make competing moves of its own.
- One agent manages subscriptions: the one the person asked, or the fleet studio's agent once that bundle is available. There's no other fleet-manager role and no automatic manager. Use the commands below directly; no extra hand-off is needed.
- The Observatory is optional: it can show readings, but you don't need it to rotate.
The unit is a Codex home, with every seat on it. Moving some seats on a shared home to a different account, and
moving Claude seats, aren't supported moves yet: say so instead of improvising. A Codex seat with a home of its own
moves alone, with rig provider switch --seat (above).
-
Load the person's account policy.
- It's the context pack named
account-policy: checkrig context listfor it, then read it withrig context get account-policy. Its manifest'sversionis the version you follow and report. - Follow it, and record the version in each report. A line the person approved ("move the build box's seats without asking", "never move the reviewers") is standing authorization: act on it, don't ask again.
- A line grants only what the person actually gave. "Agents may do sign-ins" is usable only where a sign-in route and access already exist; otherwise ask, and record that the preference couldn't be used.
- Something the policy doesn't cover: use your judgment or ask the way the policy says, record why, and propose a one-line addition.
- No policy is never a reason to stop: work from this section, report the policy as
{ "none": true }, and offer once to write one with them (below).
- It's the context pack named
-
Know the subscriptions.
- Each subscription is reached through a saved profile on a home (
rig auth listandrig auth save) on each machine. Which seats use which profile isrig auth seats, and that's metadata: not proof of a live account. - Check you can read each subscription's usage, and name any you can't.
- Each subscription is reached through a saved profile on a home (
-
Read before deciding.
- Use
rig provider status --all-hosts --json(add--refreshfor fresh native readings). Each allowance has its windows, reset times and when it was observed, and when it was last observed exhausted. - One allowance seen on two machines is one allowance: never add its headroom twice.
- A stale reading is usable only with its age recorded, or after a refresh.
- An unknown or unavailable reading is never observed room.
- After a known reset, a window carries an
expectation(expected full since that reset). You may plan on it, but it isn't measured: report it as expected, and take a fresh reading when you can. - Equal reset times don't mean the same account.
- A seat's link to a subscription is observed, configured or unknown. Only an observed link shows the seat actually uses it; a configured one (a profile, a registry row) is a claim.
- A sign-in state of
unknownisn'tneeds_sign_in. An otherwise authorized move can go ahead, and its uptake evidence settles it. A knownneeds_sign_inis step 6.
- Use
-
Decide, and leave seats alone for a reason.
- Balance, don't chase: spread machines across the subscriptions with room rather than moving every machine onto
the one that just reset.
rig usage topon each machine shows which work hardest: a high-use machine is worth a subscription of its own, and lower-use machines can share one. Keep a backup where the policy says so. rig seat restart <seat> --keep-conversation --planshows what a restart keeps, and every peer on the same home. Moving the home moves them all.- Skip a seat that's mid-turn, running a service, or about to finish, and record why.
- Balance, don't chase: spread machines across the subscriptions with room rather than moving every machine onto
the one that just reset.
-
Move the home.
rig auth switch <profile> --runtime codex --codex-home <that home>, on that machine, or from here with--host <that machine's SSH entry>. Without--codex-home,rig authacts onCODEX_HOME, or on the default home;rig auth status --codex-home <home>(with--hostfor another machine) prints which home it means: check it before switching. A remote switch always names its home, and copies a profile already saved there.- Another machine is reached through its SSH entry in
rig host list. An entry registered only over HTTP can't run these commands; use the machine's SSH entry, or run them on that machine. - Then restart each chosen seat:
rig seat restart <seat> --keep-conversation --reason "<why>", with--host <id>for a seat on another machine (the restart runs there, and its audit names you). If the connection drops, the answer isremote-outcome-unknown: the switch or restart may have run there, so read what it names (for a restart,--plan'slastRestart) before anything else. - After it completes a real tool-using turn, check it:
rig seat restart <seat> --keep-conversation --verify --expect-account <the target's Account line>.--verifyonly checks; it never restarts. Only that check reportsmoved, and anything less is unconfirmed. - A slow reply isn't a failure: never restart a seat a second time. Follow up at your next look.
-
Report every move, partial or unconfirmed included, with
rig provider moves report --file <move.json> --to <whoever asked>(--helpshows the shape). It becomes a queue item to them; the Fleet app reads these reports once its page is connected to them. It holds:- the home and machine, and from and to, as labels;
- your reason, and the policy version you followed (or
{ "none": true }); - each reading you acted on,
observedwith its observation time,expectedwith the reset it rests on, orunavailable; - each seat's result:
movedonly with the verify check's matched account and observed tool turn, kept as separate facts; otherwiseunconfirmedorunknown, with what's missing; - the peers, for example "not restarted; account unverified".
A home switched with every seat left as a peer is still a move to report. A follow-up that confirms an earlier unconfirmed seat is a new report with
--follows <the earlier report's qitemId>. A report to a person's seat names its evidence with--evidence-ref(for example, the saved--verify --jsonoutput). Never put credentials in a report: a value in a recognized credential format is refused. A refused report changes nothing about the move: correct it and file it again; never repeat the switch or a restart because a report was refused. Asked "why did these seats move?", even after your own restart, answer fromrig provider moves --seat <seat>. It reads the newest reports (50 by default,--limitup to 200) and then filters them, so it's a recent window, not a seat's whole history: raise--limitwhen you need older ones. -
When a sign-in is needed:
- Ask the person once, durably: a queue item to their seat, naming the machine and the subscription.
- Move nothing onto that subscription until it's done.
- On later wakes, check that item instead of asking again. Carry on with everything else.
-
When nothing has room: say so, with the reset times. Never fall back to an API key, buy anything, or create an account.
What wakes you: a watchdog, a usage-limit park on the queue, or the person asking. How often you look is your judgment within those.
If there's no policy yet. Start from what you can see, and ask only what you can't; keep it to a few minutes, and never run it as a form. "Whatever you think" or "skip" is an answer: write down the default you'll use.
-
Look first:
rig auth listandrig auth seatson each machine you can reach,rig provider status --all-hosts --json, andrig usage topon each machine. Draft what you can. -
Confirm what you saw, in one message ("three Codex subscriptions and one Claude one, on two machines; the build box uses about four times the tokens of the laptop; any I can't see?").
-
Ask how they want them used, offering the common shape: the busiest machine gets its own subscription, the rest share, and one stays spare.
-
Ask what you may do on your own: move a machine's Codex seats without asking; machines or seats to ask about first or never move; sign-ins (may agents, and through which browser or profile, or always ask). Say what's supported today: moves are by Codex home, Claude moves aren't supported yet, and a sign-in preference is used only where a sign-in route and access exist.
-
Ask how to reach them (their seat's queue, a chat channel, the Fleet app), for what, and what to do when they don't answer. A channel they name is a preference: check the route with
messaging-the-human, and use a queue item to their seat if it isn't ready. -
Ask what to do when things go wrong: nothing has room, a sign-in is needed while they're away, a move didn't confirm.
-
Show the draft in plain words, let them correct it, and save it as a context pack named
account-policyin the context library where you run (rig config get context.root): a folder withmanifest.yaml(version: "1",name: account-policy,taxonomy: world, apurpose, andfiles: [{ path: POLICY.md, role: policy }]) andPOLICY.md, installed withrig context add <folder>and read back withrig context get account-policy. To share one policy across machines, use a Git repository:rig context add <repo> --git. Labels and references only: never tokens, passwords, cookies or recovery codes.POLICY.mdhas these parts, in their words: subscriptions (label, provider, plan, how each is reached); how they want them used; what agents may do on their own; how to reach them; when things go wrong; and a dated Changes list. Fill it from what you saw and what they said, never from another person's setup.
Changing the policy. When they say what changed, copy POLICY.md aside as the before copy, edit it, add a dated
line under Changes, and raise version in manifest.yaml. Keep one place where it's edited: edited where it's
installed, run rig context sync; kept in a separate folder, replace it with rig context rm account-policy and
rig context add <folder>; Git-backed, edit and commit in the checkout rig context source inspect account-policy
reports, then rig context source update account-policy (it never pushes). Read it back with
rig context get account-policy; your next decision follows the new version.
Context packs and paced delivery (0.5.0)
Compose context once, hand it to a seat cleanly. A library primitive plus a set of delivery flags. The rule that keeps the grammar coherent — internalize this one: the noun stores and composes; the verbs deliver. rig context never sends anything; delivery is only ever rig send / rig broadcast / rig walk / rig queue.
Version note: this describes the library surface introduced in 0.5.0. Check the installed command and serving daemon before relying on it. In 0.4.x, bare
rig contextwas a context-window usage viewer (above); in 0.5.0 that viewer is removed and therig contextname belongs to the library here.
rig context — the store + compose library (never delivers)
Manage and compose context (any text/markdown) into reusable packs. Every piece and pack has a stable, path-like ref — you address context the way you address files (skills/claude-compaction-restore, reference/rig-spec.md).
rig context list # what's in the library
rig context show <ref> # read a piece or pack
rig context add <source-dir> # install an existing pack directory into the store
rig context preview <ref> # assemble + show a pack WITHOUT delivering it
rig context sync # re-walk discovery roots, refresh the library index
rig context rm <ref>
rig context compose --out packs/<ref> --from <fileA> <fileB> ... # ordered pieces -> a durable pack
- Sensible default store location; works unconfigured, can be pointed elsewhere later (another folder now; a machine or URL later).
compose(v1) is honest ordered concatenation of named files into a durable pack with a ref — "here's a file, read a file."- No delivery verb lives on the noun. To get a pack to a seat, hand its ref to a delivery verb below.
rig walk — paced delivery of a sequence
rig walk <seat> --through <ref | file ...> --pace 10s
Walk a seat through a pack: each piece is sent into the pane, spaced by --pace, so the agent processes between sends (the human paste → wait → paste rhythm). Its own top-level verb, push-direction — the walker leads and does not wait for replies; the spacing does the work. Reach for walk on onboarding, repriming, or a fleet update — anything absorbed in order rather than all at once.
The delivery grammar — send a ref, walk a pack, or attach it to a qitem
| When | Verb |
|---|---|
| One thing, now | rig send <seat> --context <ref> |
| One thing, everyone | rig broadcast --rig <rig> --context <ref> |
| A sequence, absorbed | rig walk <seat> --through <ref> --pace 10s |
| Context riding a durable handoff | rig queue create … --body-context <ref> |
- Rule of thumb: small piece →
send --context; real pack →walk. An oversizedsend --contextwarns "this is walk-sized" instead of blasting the pane. --body-contextsnapshot rule: a qitem built from a ref stores the resolved content in its body plus the ref for provenance — the handoff carries what was actually sent, and a later library edit never silently rewrites a past handoff's history.- The orchestrator habit — assign work with its context attached:
Replacerig context compose --out packs/qitem-brief --from <brief-file> <proof-file> rig queue create --destination dev-driver@build --body-context packs/qitem-brief \ --summary "…" --tags "project:<id>,mission:<id>,slice:<id>"<brief-file>and<proof-file>with your existing local files. The curated context rides the durable handoff, survives compaction, and is auditable.
Skills tier vs context tier: skills are the HOT tier (ambient, finite, always-visible front-matter); context packs are the COLD tier (unbounded, fetched on instruction — "read rig context get onboarding-width"). Don't overrun the skill layer by using skills as context packs — that's what this primitive is for.
Lifecycle
Bring a rig up
rig up <source>
rig up <source> --plan
rig up <source> --yes
rig up <source> --cwd /path/to/project
rig up <source> --existing
rig up <source> --fresh <seat...>
rig up <source> --json
<source> can be:
- a rig spec path
- a
.rigbundlepath - a bare name
Bare names are special:
- if they match a library spec,
rig uplaunches from the spec library - if they do not match a library spec,
rig uptreats the name as an existing-rig restore/power-on target - if both exist,
rig upfails loudly on ambiguity
Resume-original-by-default (v0.3.4+):
- For an existing rig,
rig up <name>resumes each seat from its original session/snapshot by default (operation A). Seats that successfully resume reportresumed. --fresh <seat...>is the per-seat opt-in for deliberate fresh-prime (operation B). Named seats are reported asfresh-primed.--existingforces existing-rig restore semantics on a bare name, bypassing library-spec resolution. Useful when a rig name collides with a library spec name.- Example:
rig up --existing my-rig --fresh dev-impl— resume everything inmy-rigexceptdev-impl, which is freshly primed. - Seats with no resumable session land in
awaiting-decision(zero-session honest state, NOTfailed); see the five-term restore vocabulary in "Recovery and Resilience" below.
--plan (v0.3.4+):
rig up <source> --planproduces a read-only restore plan preview. It surfaces per-seat resume/fresh-prime intent and any awaiting-decision seats without mutating state. Honest async timeout: a stuck plan reports the timeout rather than hanging silently.
Current behavior notes:
--target <root>is only for.rigbundle/ package installation. It does not change agent cwd.rig up --cwdis shipped.rig up --cwd <path>sends a per-run cwd override for all members in that launch.local:agent_refvalues resolve relative to the rig spec directory, not your shell cwd.- if you copy a built-in spec elsewhere, keep its
agents/tree beside the YAML or rewrite those refs topath:/absolute/path rig specs add <directory>installs a full spec tree when the directory containsrig.yamloragent.yaml.- Permission policy: a rig carries a permission policy and boots with it (never changed on the fly). Default if none set = the minimum floor (Claude
acceptEdits/ Codex workspace-only / Pi--no-approve); otherwise a chosen built-in (Locked / Standard / Open) or deliberately none. When you spec or bring up a rig, decide its policy — apply it viaapplying-a-permission-policy(see also the onboarding menu, "Permission policy — pick one at setup").
Legacy/spec-specific surfaces still ship too:
rig bootstrap <spec> [--plan] [--yes] [--json]
rig requirements <spec> [--json]
Tear a rig down
rig down <rig> # <rig> = rig name or id (active rig)
rig down <rig> --snapshot
rig down <rig> --delete
rig down <rig> --force
rig down <rig> --json
If --snapshot succeeds, human output includes the restore hint.
Archive a stopped rig (recoverable) — v0.3.3+
rig archive <rig> [--json]
rig unarchive <rig> [--json]
rig archive marks a stopped rig as archived (sets archivedAt) without
discarding it. The rig is preserved for later restoration via rig unarchive,
which clears archivedAt and returns the rig to the active set.
Archive vs delete:
rig down --delete— permanent removal; not recoverable.rig archive— recoverable; the rig is hidden from the default active view but its record + snapshots are preserved.
Visibility in rig ps:
rig ps— active rigs only (default).rig ps --include-archived— includes archived rigs, marked with*.
SSE events rig.archived / rig.unarchived drive Project / dashboard updates;
consumers that depend on the rig list should subscribe rather than poll.
Environment services
rig env status <rig>
rig env logs <rig> [service]
rig env down <rig>
Use these for service-backed rigs and agent-managed apps.
For secrets-manager, these are the fastest CLI surfaces for:
- confirming whether Vault is healthy
- reading Vault container logs
- stopping the Vault env without tearing down the specialist session first
Release management without killing live claimed sessions
rig release <rigId>
rig release <rigId> --delete
rig release <rigId> --json
Use rig release for adopted/claimed-session rigs when you want OpenRig to stop managing the rig but leave the tmux sessions alive.
This is the safe recovery/reset surface for the "sessions still exist, management is broken or stale" case.
If the rig contains OpenRig-launched nodes, rig release refuses loudly instead of pretending the mixed rig is safe to detach.
Snapshots and restore
rig snapshot <rigId>
rig snapshot list <rigId>
rig restore <snapshotId> --rig <rigId>
rig restore requires --rig <rigId>.
Claude Code autonomy note:
- unattended
rig whoamion boot may require the local permission allow list to includeBash(rig:*)
Import/export and bundles
rig export <rigId> -o rig.yaml
rig import <path> [--instantiate] [--materialize-only] [--preflight] [--target-rig <rigId>] [--rig-root <root>]
rig bundle create <spec> -o out.rigbundle
rig bundle inspect <bundle>
rig bundle install <bundle> [--plan] [--yes] [--target <root>] [--json]
Legacy package surface
This still ships, but is explicitly marked legacy:
rig package validate <path>
rig package plan <path> [--target <dir>] [--runtime <runtime>] [--role <name>]
rig package install <path> [--target <dir>] [--runtime <runtime>] [--role <name>] [--allow-merge]
rig package list
rig package rollback <installId>
Discovery and Topology Mutation
Discover unmanaged tmux sessions
rig discover
rig discover --json
rig discover --draft
Bind a discovered session
rig bind <discoveredId> --rig <rigId> --node <logicalId>
rig bind <discoveredId> --rig <rigId> --pod <namespace> --member <name>
There is no shipped top-level rig claim command.
The current adoption surface is discover, bind, adopt, and unclaim.
Self-attach the current shell or agent
rig attach --self --rig <rigId> --node <logicalId>
rig attach --self --rig <rigId> --node <logicalId> --print-env
rig attach --self --rig <rigId> --pod <namespace> --member <name> --runtime <runtime>
Use rig attach --self when the current agent should attach itself directly instead of going through discover + bind.
Current proven behavior:
- inside
tmux: attaches as a normal tmux-backed node, preserving inboundrig send/rig capture - outside
tmux: attaches asexternal_cli --print-envprints theOPENRIG_NODE_IDandOPENRIG_SESSION_NAMEexports for the current shell
Recommended flow:
rig attach --self --rig <rigId> --node <logicalId> --print-env > /tmp/openrig-self-attach.env
. /tmp/openrig-self-attach.env
rig whoami --json
Notes:
- for tmux-backed self-attach,
rig whoami --jsonis the right verification - for raw/external self-attach,
rig ps --nodes --jsonis currently the more reliable verification surface - if the current shell is outside tmux, pass
--display-name <name>when you want a stable human session label recorded - to read a seat's identity or state later, use
rig whoamiorrig seat status, notrig attach --jsonoutput or a file holding a seat's environment. Don't printOPENRIG_ACTIVITY_*values: the activity token is a credential, and it doesn't identify the seat
Adopt a topology and bind live sessions
rig adopt <path> --bind <logicalId=tmuxSessionOrDiscoveryId>
rig adopt <path> --bind <logicalId=...> --bind <logicalId=...> --json
rig adopt <path> --bindings-file <bindings.yaml>
rig adopt <path> --bind <logicalId=...> --target-rig <rigId> --rig-root <root>
Use rig adopt when the sessions already exist and you want OpenRig to start managing them.
A bindings file is the durable map from authored logical IDs to live sessions. Shape:
bindings:
dev1.impl2: dev1.impl2@rigged-buildout
dev1.qa: dev1.qa@rigged-buildout
Spec + bindings is the proven recovery pair for adopted rigs. Spec gives OpenRig the intended topology. Bindings tells OpenRig which discovered live session belongs in each logical node.
Proven adopted-rig recovery workflow
This workflow is proven for the case where the external tmux sessions are still alive:
rig release <rigId> --delete
rig discover --json
rig adopt <spec.yaml> --bindings-file <bindings.yaml>
What this does:
- removes OpenRig management without killing the sessions
- re-discovers those same sessions as unmanaged
- re-attaches them to the topology defined by the spec + bindings
Important limits:
- this is for
sessions still alive - spec alone is not enough for adopted rigs; you also need bindings
- this does not yet mean OpenRig can recreate dead external sessions from nothing
Add unmanaged pods into an existing rig
This is the proven workflow when a rig is already managed, but a new pod was created outside OpenRig and you want to add it later:
rig adopt <pod-fragment.yaml> --bindings-file <pod.bindings.yaml> --target-rig <rigId>
Use this when:
- the target rig already exists
- the new sessions are live and visible in
rig discover --json - you want additive topology growth, not a full rebuild
What to prepare:
- a pod fragment spec with only the new pod
- a bindings file mapping the new logical IDs to the live session names
Verification loop:
rig discover --json
rig adopt <fragment.yaml> --bindings-file <bindings.yaml> --target-rig <rigId>
rig ps --nodes --rig <rigId> # the target rig's nodes (--nodes alone = your current rig)
rig export <rigId> -o rig.yaml
Success looks like:
- the new sessions stop appearing in
rig discover - the new logical IDs appear in
rig ps --nodes --rig <rigId> rig exportincludes the new pod
Mixed-origin rigs are allowed
One rig can contain both:
- adopted nodes bound from already-running sessions
- OpenRig-launched nodes created later with
rig expand/rig launch
Current safety rule:
rig releaseis for claimed/adopted-only rigs- if a rig contains launched nodes,
rig releasefails withcontains_launched_nodes
Manager-assisted recovery
The proven operator pattern is:
- keep one OpenRig manager session outside the rig it manages
- address the target by rig name, not cached rig ID
- find the target rig with bare
rig ps(lists all rigs), then resolve its owner fromrig ps --nodes --rig <target>(a bare--nodesread is your current rig only, not the target's) - send the manager the spec path, bindings path, and verification steps with
rig send
This lets ordinary agents ask the manager for OpenRig help instead of every agent needing to be an OpenRig expert.
Add/remove running topology parts
rig grow <rig-id> <member...> [--pod <pod> | --new-pod <pod>] [--runtime <runtime>] [--cwd <path>] [--json]
rig expand <rig-id> <pod-fragment-path> [--rig-root <path>] [--json]
rig launch <rig> <nodeRef> [--json]
rig launch <rig> --seats <a,b,c> [--hold-reason <text>] [--json]
rig remove <rigId> <nodeRef> [--json]
rig shrink <rigId> <podRef> [--json]
rig unclaim <sessionRef> [--json]
rig grow is the simplest way to add seats: no YAML, the default agent spec, and one
runtime and working directory for the named seats (--new-pod creates a pod for them).
Check rig grow --help on your installed version. Use a fragment with rig expand (a pod)
or rig add (one member) when a seat needs an explicit model, a permission policy, a
different agent spec or role profile, per-seat runtime or cwd, or startup files.
Node-granular managed partial restore (v0.3.4+):
rig launch <rig> <nodeRef>launches a single seat that isn't running, by logical id or node id, through orchestration.<rig>is the rig's name or ID; a name shared by more than one active rig stops with the candidate IDs. A seat that's already running is left as it is; for a fresh occupant there, userig seat launch <seat> --fresh --stop --reason <why>.rig launch <rig> --seats <a,b,c>launches a comma-separated subset of seats.--hold-reason <text>records a reason for holding non-target seats during the partial launch.- This is a SUPPORTED managed path. The prior
pod_aware_launch_unsupporteddead-end is retired; pod-aware narrow launch now goes through this surface rather than ad-hoc rebuilds.
Add a member to an existing pod — v0.3.3+
rig add <rig> <pod-namespace> <member-fragment-path> [--json]
rig add is the top-level verb for the add_member converge op. It adds a
single member to an existing pod from a YAML/JSON member fragment file. The
daemon resolves the named pod, validates the member, runs preflight, and
launches the member in place.
<rig> is the rig id or its exact name (the rigName that rig whoami --json
reports). A name shared by more than one rig is refused before anything is
added, with the matching ids listed (rig_ambiguous under --json). rig add
always targets the local daemon: while a remote host is selected it refuses to
resolve a name (remote_host_selected); an exact local id still works.
HTTP outcomes:
201— member added; per-node launch state included in the response.400—validation_failedorpreflight_failed(the fragment or its launch posture is rejected before any state change).409—member_conflict(a member with that identity already exists in the pod).
Use rig add when you want additive growth inside a pod without re-running
the full rig expand pod-fragment path or rebuilding the rig.
Specs and Validation
Validate specs
rig spec validate <path> [--json]
rig spec preflight <path> [--rig-root <root>] [--json]
rig agent validate <path> [--json]
Spec library
rig specs ls [--kind <kind>] [--json]
rig specs show <name-or-id> [--json]
rig specs preview <name-or-id> [--json]
rig specs add <yaml-or-directory> [--json]
rig specs sync [--json]
rig specs remove <name-or-id> [--json]
rig specs rename <name-or-id> <new-name> [--json]
MCP
rig mcp serve [--port <daemon-port>] # stdio: a local app starts the server itself
rig mcp serve --http [--bind <host:port>] # Streamable HTTP at /mcp (default 127.0.0.1:7437)
rig mcp token add <name> --seat <session> [--rig <name>...] [--root <name>...] [--workspace] [--owner]
rig mcp token list [--json]
rig mcp token revoke <name>
The 36 shipped tools fall into three groups. Over stdio, and over HTTP with no grants, every tool is offered. An HTTP
grant offers the work tools; the workspace tools with --workspace or --owner; and the owner tools only with
--owner, which offers every tool.
- Work (19): orientation, communication and the durable work loop.
rig_whoami: the seat the server acts as (rig, pod, peers, edges) and thecaller: authenticated (an HTTP credential, with its principal) or declared (OPENRIG_SESSION_NAMEwhere the server was configured, which nothing verifies).rig_status,rig_ps,rig_rig_nodes, andrig_team: a rig's lead (its onedelegates_toroot, the seat to delegate to) and each seat's role, delegation and activity.rig_send,rig_capture,rig_chatroom_send,rig_chatroom_watch.rig_queue_create,rig_queue_show,rig_queue_list,rig_queue_claim,rig_queue_update,rig_queue_handoff,rig_queue_unclaim:reviseson create asks for a linked revision of an earlier item;- show names a handed-off item's successor;
handedOffFromon list follows a handoff;- handoff passes an item this seat holds on to the next seat in one step, as
rig queue handoffdoes: the new item carries the work's lineage; - unclaim puts back a claimed item this seat won't finish.
rig_artifact_read: a finished item's artifact (itsevidenceRef) or a named file, through the daemon's shared roots (OPENRIG_FILES_ALLOWLIST), in windows (offset,nextOffset). Outside those roots it answers unavailable rather than reading a local path.rig_human_decision_room, thenrig_queue_decide: on your own pending ask to a person, read the decision room of the mode they have on, and record a decision you made for them inside it. No tool writes a person's own answer.
- Workspace (4):
rig_slices,rig_mission,rig_context_pack, andrig_context_get(one context file or section, asrig context getreads it). - Owner (13):
rig_up,rig_down,rig_snapshot_create,rig_snapshot_list,rig_discover,rig_bind,rig_add,rig_bundle_inspect,rig_agent_validate,rig_rig_validate,rig_telemetry_events(one bounded page of event metadata), and:rig_restore, which starts a restore and returns its attempt;rig_restore_status, that attempt's receipt.
Results:
- A structured result comes beside the text from the queue tools,
rig_whoami,rig_team, the four workspace tools,rig_artifact_read,rig_human_decision_roomandrig_restore_status. The other tools answer in text. - Every failure carries the same structured shape:
ok: false, acodeand amessage.
Identity:
- Over stdio, and over HTTP with no grants: the server acts as one seat, the
OPENRIG_SESSION_NAMEin its environment. Every request carries that identity, so no tool call names a sender. Without it, writes that need an actor are refused withactor_required. - Over HTTP with grants: each caller acts as its own grant's seat.
Over HTTP: an assistant that can't start a local process
- No grants (no token file):
- the listener stays on loopback and acts as
OPENRIG_SESSION_NAME, declared, like stdio; - a request whose
Hostisn't loopback is refused.
- the listener stays on loopback and acts as
- With grants: every request needs
Authorization: Bearer <token>, loopback included, because a tunnel or proxy delivers remote traffic to loopback.- Identity: each caller acts as its grant's seat.
- What it reaches: only its rigs' seats and work, and its roots' artifacts. Anything else answers
out_of_scope. - Bad credentials: a missing, invalid or revoked token gets a 401, never the environment's seat.
- A broken token file: the server reads the file on every request. While it is missing or can't be read, every
request is refused (
auth_config_unreadable); restore a valid, readable file and requests are served again, with no restart. The other way back is a restart with no token file, which returns the server to the no-grants mode above (loopback only). A restart never skips a token file that is present: its grants still apply.
- Beyond loopback (
--bind), the server starts only with a grant. TLS comes from the tunnel or proxy, not from this server. rig mcp token addprints the token once and stores only its hash.revoke: the caller's next request is refused, and work it already handed over stays with its owners.- Grants, binds and tunnels are the person's decisions. Give them the commands, and don't run them on their behalf.
Join a team from an agent with no terminal of its own (desktop apps)
An agent in a desktop app can work with a team as a seat through this MCP server. If the app gives it a shell, it can
use the rig CLI instead, the same way.
- Attach once. With its shell, or by asking the person to run it:
rig attach --self --rig <rigId> --pod <namespace> --member <name> --runtime <runtime> --print-env. Note theOPENRIG_SESSION_NAMEit prints. - Give the person the MCP entry to add. Don't edit their app's settings yourself. The entry runs
rig mcp serve, withOPENRIG_SESSION_NAME=<that seat>in its environment. Most MCP clients take it as a command, arguments and anenvmap. An app that can't start a local process connects torig mcp serve --httpinstead, with a grant for that seat (see "Over HTTP" above). - Hand off work with
rig_queue_create, and keep theqitemIdit returns. - Read the answer with
rig_queue_show <qitemId>. The item's state, closure and transition notes are where the assignee records it, andrig_artifact_readreads an artifact it names. A call that times out has an unknown outcome: checkrig_queue_showwith the same id before you retry, and retry only with that id. - Take work with
rig_queue_list(owned: true),rig_queue_claimandrig_queue_update. Close with the answer innote. To pass it to the next seat, userig_queue_handoff; to put back one you won't finish, userig_queue_unclaim.
Limits today:
- OpenRig can't wake this agent itself.
rig sendto an attached external node is refused, and a task's nudge to it is unroutable. It sees new work when it checksrig_queue_list, or, if its app can run background shell commands, with the opt-in wait below. - Proven surfaces: treat each app, and each mode inside one, as unproven until it has been run there.
Wake on new work (opt-in; off unless the person turns it on)
An agent whose app can run a shell command in the background, and starts a new turn when that command finishes, can wait for its own work. Claude Code, including Claude Desktop's Code tab, does this.
- Only if the person has said they want teammates to be able to wake this session. Never turn it on yourself, and never change their settings for it.
- The shell needs this seat's identity: export the
OPENRIG_SESSION_NAMEthat the one-time attach printed. - Start this as a background command, then end your turn. Don't poll it yourself.
# Waits until a task is assigned to this seat, prints it and exits; ends after MAX seconds.
max=${MAX:-3300}; every=${EVERY:-5}; end=$(( $(date +%s) + max )); checked=0; failing_since=""
while [ "$(date +%s)" -lt "$end" ]; do
if out=$(rig queue list --owned --state pending --json 2>/dev/null); then
checked=1; failing_since=""
case "$out" in *'"qitemId"'*) printf 'NEW WORK: %s\n' "$out"; exit 0 ;; esac
elif [ -z "$failing_since" ]; then
failing_since=$(date -u +%H:%M:%SZ)
fi
sleep "$every"
done
why="Run rig queue list --owned to see why (this seat's identity, the daemon) before starting the wait again."
if [ "$checked" = 0 ]; then
echo "Couldn't check for work: every poll failed. $why"; exit 1
elif [ -n "$failing_since" ]; then
echo "Checking stopped working at $failing_since: every poll since then failed. $why"; exit 1
fi
echo "No new work before the deadline. Start the wait again."
- When it wakes you with
NEW WORK:rig queue claimeach item, do it, and close it with the answer. Then start the wait again and end your turn. - Don't leave an item you've seen pending: it wakes you again at once. Claim it, block it or hand it off.
- A failed poll doesn't end the wait; only new work or the deadline does.
- If every poll failed, or every poll since some point did (the daemon stopped mid-wait), it says so, with when,
and exits non-zero instead of reporting no work. A failure followed by a successful poll stays quiet. Find out why
with
rig queue list --owned(usually this seat's identity, or the daemon) before starting the wait again. - If your app stops the background command, or you start a new session, start the wait again.
Troubleshooting and Weird States
When the CLI behaves strangely, use the smallest truthful check first:
rig whoami --json
rig daemon status
rig ps --nodes --json
Specific operator rules:
Sent to ...+Verified: nois ambiguous delivery, not automatic failure. Check reply,rig capture, transcript evidence, or queue/outbox state before retrying.- partial
rig whoami --jsoncan happen when identity is still inferable but the daemon-backed path is degraded. - the unified-exec-process warning is a host/tooling-layer signal, not automatic proof that the OpenRig topology is unhealthy.
If you hit the unified-exec warning, inspect for stale one-shot helpers before touching live seats:
ps -axo pid,ppid,command | rg 'tmux send-keys|rig queue create|tmux attach|codex|claude'
Safe cleanup target:
- orphaned one-shot wrappers like
tmux send-keys ...
Do not mass-kill:
tmux attach ...codex ...claude ...
JSON and Error Posture
Design assumptions that hold in the shipped CLI:
- many operator commands support
--json - error messages are intended to say what happened, why it matters, and what to do next
- daemon-backed commands fail loudly when the daemon is stopped or unhealthy
- restore failure is not something you should silently reinterpret as success
After-Compaction Recovery Checklist
rig whoami --jsonrig transcript <your-session> --tail 100rig ps— lists ALL rigs on the host (know the world FIRST); thenrig ps --nodes --rig <your-rig>for your seats. ⚠rig ps --nodes --jsonalone is your CURRENT rig only — do NOT mistake it for the whole host (a freshly-compacted agent has no other context to catch the lie).rig chatroom history <rig> --limit 50
Commands That Do Not Exist
Do not assume these exist unless the shipped help starts listing them:
rig claimrig blamerig replay