git:20260830.80d5290 to git:20260907.386e515

75 added, 146 removed. Audit A to A.

---
name: cross-host-rig-commands
- description: Use when issuing `rig` commands against a remote host via `--host <id>` flag (single-hop SSH to a host declared in `~/.openrig/hosts.yaml`). Covers the 4 structured failure modes (ssh-unreachable / permission-gate / remote-daemon-unreachable / remote-command-failed), the `--verify` honest pass-through (SSH success is NOT verify success), and the host-registry shape. v0 supports `rig send / capture / ps / whoami --host <id>`.
+ description: Use when addressing a registered remote OpenRig host, choosing its transport, or interpreting a cross-host result.
metadata:
cli_surfaces_referenced:
- capture
- daemon start
- host list
- - host show
+ - host add
+ - host doctor
- ps
- send
- whoami
openrig:
stage: factory-approved
sibling_skills:
- rig-lifecycle
- topology-mutation-and-seat-management
- seat-scaling-and-specialization
- sidecar-operator
- rig-bundles-and-shareable-artifacts
- specification-system
- extension-and-user-workspace
---
- # Cross-Host Rig Commands
-
- A first-class OpenRig surface for issuing `rig` commands against a
- remote host via single-hop SSH. v0 productizes the existing SSH-envelope
- operator pattern (`ssh <host> rig <cmd>`) with declared host identity,
- explicit cross-host invocation path, preserved `--verify` semantics, and
- 4 named failure modes.
-
- **Current-host note (2026-07-14):** this skill still preserves the original
- SSH-v0 doctrine below, but the live host registry may also contain HTTP
- read-through/tunnel entries such as `your-vm` and `your-other-vm`. Use the
- host registry entry as ground truth for transport shape, and use
- `conventions/multi-host-naming/README.md` for host-id naming. HTTP entries
- require `url` plus exactly one of `bearer_file` or `bearer_env`.
-
- **v0 is CLI-side shell-out only — `packages/daemon/` is NOT touched.**
- The remote host has its own managed `rig` available on `$PATH`.
-
- ## Use this when
-
- - Driving a Tart VM from the host (the immediate cross-host consumer)
- - Operating Mac host A against Mac host B
- - Reading remote state via `rig ps --host` / `rig whoami --host`
- - Sending or capturing on a remote rig session (`rig send/capture --host <id>`)
- - Authoring a `~/.openrig/hosts.yaml` registry entry
-
- ## Don't use this when
-
- - The target is local. Don't pass `--host` for local commands.
- - You need multi-hop SSH (host A → host B → host C). v0 is single-hop only.
- - You want reverse direction (remote initiates back to local). v0 is originator-pull only.
- - Cross-host **seat handover** (moving a durable owned seat across hosts) — deferred to higher-level primitives. (Cross-host **queue writes**, by contrast, now ship — see "Cross-host queue writes" below.)
- - The transport isn't SSH. v0 supports `transport: ssh` only.
-
- ## Cross-host queue writes (updated 2026-07-21 vs main d37a08ad)
-
- Cross-host **queue writes** are no longer deferred — they ship — but the rule
- differs from the interactive verbs (`send`/`capture`/`ps`/`whoami`):
+ # Cross-host rig commands
- - **Queue writes are EXPLICIT-only.** Address the target host explicitly with
- `--host <id>` or the `member@rig@<host>` form. A queue write **never** follows a
- persisted host selection.
- - **`host select` stickiness does NOT apply to queue.** `host select` affects the
- `resolveEffectiveHost` verbs (`send`/`capture`/`ps`/`whoami`) — those follow the
- selected host — but a queue write ignores it and requires explicit addressing.
- - **Host address parsing differs** between the interactive verbs and the queue
- verbs; do not assume the interactive form carries over. When unsure, address the
- host explicitly.
+ Use a registered host address and the entry's declared transport. SSH entries
+ run a remote CLI through a single-hop shell; HTTP entries use the remote daemon.
+ These paths have different prerequisites and command coverage. Check the exact
+ command's help and the target installation before a consequential operation.
- ## Host registry shape (`~/.openrig/hosts.yaml`)
+ ## Choose and inspect the destination
- Host ids should follow the OpenRig-work multi-host naming convention:
- `conventions/multi-host-naming/README.md`. In this environment that means
- physical hosts such as `your-vm`, local OpenRig development VMs such as
- `your-vm`, persistent product VMs such as `your-other-vm`, and VPS hosts such
- as `your-vps`. Keep old aliases during migration until no live routing
- depends on them.
+ `rig host list` shows registered hosts. `rig host add --help` describes registry
+ writes; `rig host doctor --help` describes reachability checks. Inspect the
+ configured OpenRig home rather than assuming the default `~/.openrig/hosts.yaml`.
+ Registry changes affect later routing, so make them within the requested scope.
```yaml
hosts:
- - id: a-test-vm
- transport: ssh # v0 supports "ssh" only
- target: a-test-vm.local # DNS name, SSH config alias, or IP
- user: example-user # optional
- notes: "Tart VM" # optional
- - id: laptop-b
+ - id: test-vm
transport: ssh
- target: laptop-b.tail-scale-net
+ target: test-vm.local
user: example-user
+ - id: remote-dev
+ transport: http
+ url: http://remote-dev.example:7433
+ bearer_env: REMOTE_RIG_TOKEN
```
- Validation rules:
-
- - `hosts` required, non-null array
- - Each entry: `id` required (non-empty, unique), `transport` required (`ssh` only), `target` required
- - `user` and `notes` optional
- - Operator-managed file; v0 does NOT include any sub-command to add/remove/list hosts (operators edit YAML directly)
- - Missing or invalid file returns a clear error pointing at the canonical path
+ The registry requires a hosts array, unique non-empty ids and a supported
+ transport. SSH needs a non-empty target; user and notes are optional. HTTP needs
+ a URL and accepts at most one of bearer_env or bearer_file. Omitting both is
+ valid for a tokenless target. A configured but unavailable bearer is a permission
+ failure, not anonymous fallback. Host ids also have reserved-name validation;
+ use the actual validator's error rather than inventing an alias that collides.
- ## CLI surface (v0 shipped)
+ ## Address the intended operation
```bash
- rig send <session> "msg" --host <id> --verify
- rig capture <session> --host <id>
- rig ps --host <id> [--nodes] [...]
- rig whoami --host <id>
+ rig send dev-worker@example-rig "check the assigned result" --host remote-dev --verify
+ rig capture dev-worker@example-rig --host remote-dev
+ rig ps --host remote-dev --nodes --json
+ rig whoami --host remote-dev
```
- Forwards every shaping flag (`--nodes`, `--full`, `--limit`, `--fields`,
- `--summary`, `--filter`, `--json`) to the remote `rig` invocation. The
- remote rig's output is verbatim passthrough on success.
-
- ## The 4 structured failure modes (load-bearing API contract)
-
- The CLI distinguishes 4 failure modes; operators get an actionable error
- per mode; JSON output preserves the `failedStep` enum:
-
- | Mode | Cause | Action |
- |---|---|---|
- | `ssh-unreachable` | SSH itself failed (connection refused, host key mismatch, DNS failure, timeout) | Verify SSH access and the registry entry |
- | `permission-gate` | SSH hit auth/permission gate (Permission denied, Keychain) | Error includes hint to keychain-over-SSH field note (L4-3 D6) |
- | `remote-daemon-unreachable` | SSH succeeded but remote `rig` reported the remote daemon was not reachable | `ssh <target> rig daemon start` |
- | `remote-command-failed` | SSH succeeded but remote `rig` exited non-zero for some other reason; remote stderr is surfaced | Read remote stderr; debug remote command |
-
- Each is distinct and routable. Don't conflate them.
-
- ## `--verify` honest pass-through (load-bearing)
-
- `--verify` against a remote target must:
-
- - **Propagate** the verification request to the remote daemon (or remote `rig` invocation that talks to it)
- - **Bring back a structured verification result** (true/false + reason if false), NOT just SSH exit code 0
- - **Distinguish "SSH succeeded but verify returned false" from "SSH itself failed"** — these are different operator actions
-
- **SSH success is NOT verify success.** The remote rig is authoritative
- on `--verify`; its `Verified: yes/no` line is surfaced verbatim. Verified
- at 3 layers (executor unit, command integration, source impl).
+ These coordination commands select SSH or HTTP from the registry entry. They
+ do not silently try another transport when one fails. A host-qualified target
+ can be convenient, but confirm its parsing and any persisted host selection
+ with the particular command. Explicit --host is preferable for consequential
+ cross-host work.
- ## Hard boundaries (do-not list)
+ Queue destinations use explicit --host or a supported host-qualified destination;
+ queue writes do not follow persisted host selection. The CLI separates the host
+ from the canonical seat in the routing envelope. Different queue subcommands
+ have different flags: check their help instead of copying a flag from send.
+ A local success message alone does not establish remote persistence or pickup.
- - **Do NOT collapse `--verify` honest result into SSH exit code.** Operators and agents rely on the distinction.
- - **Do NOT silently retry SSH failures inside the primitive.** Surface them; let the caller decide.
- - **Do NOT introduce non-SSH transports in v0.** Goal is productizing the existing shipped pattern, not replacing it.
- - **Do NOT touch host-side daemon code as part of v0.** Cross-host shape is on the originator side.
+ ## Read failures at the layer that failed
- ## Cross-host annotation
+ | Signal | What to inspect |
+ |---|---|
+ | registry-load-failed / unknown-host | Registry path, entry and exact destination |
+ | ssh-unreachable | SSH connection, target and transport diagnostics |
+ | permission-gate | The configured SSH or HTTP credentials and target policy |
+ | remote-daemon-unreachable | Target listener and actual running daemon identity |
+ | remote-command-not-found | Remote CLI installation and executable lookup |
+ | remote-command-failed | The remote operation's own status and error |
- Every cross-host invocation is observable as cross-host:
+ The exact result taxonomy depends on the path. HTTP failures expose the remote
+ status/error; SSH distinguishes the shell transport from the remote command.
+ Do not start or replace a remote daemon merely because a diagnostic suggests it;
+ first establish the target's state and the authority for that lifecycle action.
+ A timed-out write may have reached its destination. Reconcile its durable effect
+ before retrying.
- - **Operator output**: `[via host=<id> (<target>)]` annotation
- - **JSON output**: `cross_host: { host, target }` field
+ ## Verification and attribution
- Annotation is PRESENT when `--host` is set and ABSENT otherwise
- (compat regression).
+ Transport success is not delivery verification or agent consumption. SSH forwards
+ --verify and returns the remote CLI result. HTTP preserves the remote transport
+ verdict and explicitly reports that the local pane-effect check did not run
+ cross-host. Inspect that result and the destination effect required by the task;
+ never replace it with “SSH exited zero” or “HTTP returned successfully.”
- ## Currently shipped (v0) vs deferred
+ Cross-host output names the host/target; JSON envelopes carry cross_host metadata.
+ The precise envelope differs by command and transport. Preserve the underlying
+ remote result when passing evidence onward.
- Shipped at openrig `cdce3a6` (2026-04-30):
- - `--host <id>` flag on `rig send / capture / ps / whoami` (initial 2 commands; promoted to 4 at `6b7043a` same day)
- - Read-only host registry validation
- - Single-hop SSH executor with 4-mode `failedStep` enum
- - `--verify` honest pass-through
- - Cross-host annotation (operator + JSON)
- - Daemon untouched
+ A target's host suffix selects a destination; a sender's origin identifies where
+ a reply belongs. Use the rendered reply address and verify its host mapping.
+ Current send derives the sender from the executing seat context, not a caller's
+ --from assertion. It adds origin-host identity at cross-host forwarding; do not
+ assume local sends have the same suffix. Unknown identity remains unknown.
- Deferred:
- - Tier 2 real-runtime cross-host proof (disposable Tart VM cycle)
- - `rig host list / show` read-only sub-namespace (only if host count grows past comfortable manual editing)
- - Non-SSH transports
- - Multi-hop SSH
- - Reverse direction (remote initiates)
- - Connection pooling/caching
+ This guide describes the implemented command paths, not proof of a live remote
+ journey on your installation. Forks, handovers and other lifecycle compositions
+ need their own supported command and scoped authority; reading remote state
+ does not authorize them.
## See also
- - `seat-continuity-and-handover` skill — host-aware seat-binding semantics for cross-host handover (deferred to v1 on top of this v0 baseline)
- - `openrig-user` skill — the local CLI surface that cross-host commands wrap
-
- ## Sender identity carries the ORIGIN host (51-09, 2026-08-06)
-
- The `@host` sugar on a TARGET is **addressing** (`member@rig@<host>` routes to that host).
- The **From:** sender now ALWAYS carries the **origin** host: a cross-host (and local) message's
- signature is **`member@rig@<originHost>`** — the sender reflects the host it was sent from, so a
- received signature names the ORIGIN, and the `↩ Reply:` hint round-trips **verbatim** back to that
- origin (never a same-named local lookalike). This is the always-suffix rule — deterministic and
- collision-proof: a signature means one thing to every receiver. (This SUPERSEDES the pre-51-09
- asymmetry — "the sender carries no `@host` suffix" — that pm@your-rig flagged as a core gap on
- 2026-07-25; 51-09 closed it.) Per BR-1 the host is NEVER folded into the session string on the
- wire: the CLI edge renders/strips it, the daemon refuses an in-band 3-part destination with a
- teaching hint, and a stale 2-part same-name destination is closed by `--host` + that teaching —
- not by any in-string magic.
+ - openrig-user — exact command reference
+ - seat-continuity-and-handover — stable seat and occupant outcomes