Skip to content

Control plane

How arbe orchestrates execution across sandboxes. For term definitions see docs/vocabulary.md.

See also: CLI, Sessions, Sandbox containers, Runs and sessions, Tasks.

Model

A run is the unit of intended work. A session is a conversation. A run contains one or more sessions — chat creates one run with one session, loop creates one run with N sessions (one per iteration), do creates one run targeting a remote sandbox.

wait, resume, and result are operations on existing runs, not new runs. You dispatch work, then observe it — the observation doesn’t create a separate intent record.

Tasks and sandboxes complete the picture. A task says why. A sandbox says where. A run ties them together with a timeline and a typed result.

State

opencode’s SQLite (~/.local/share/opencode/opencode.db, readonly) holds conversations: messages, transcripts, token usage.

arbe’s local store (.arbe/arbe.db, SQLite) holds run records and session snapshots. Runs are the source of truth for orchestration state. Session data (messages, parts) is copied from opencode on run completion for portability and sync.

Remote sessions are fetched live via the opencode SDK over sprite proxy. Not cached locally.

Status lives on runs, not sessions. Sessions are conversations — they don’t have lifecycle status beyond what opencode reports. For the raw opencode API surface (sessions, prompting, SSE events, permissions), see opencode.md.

Commands

These create runs:

arbe do <prompt|task-id> dispatches to a remote sprite. Fire-and-forget — prints run ID, returns immediately.

arbe chat [msg] runs locally. One-shot with a message, interactive TUI without. One run, one session.

arbe loop [n] [prompt] runs up to n iterations, each a fresh session within one run. Stops on completion signal, stuck detection, or permission blocks.

These operate on existing runs:

arbe wait <id> subscribes to a run’s event stream until idle or error.

arbe resume [n|id] re-opens a run’s session interactively.

arbe result [n|id] fetches the run’s result without attaching.

Inspection:

arbe runs lists runs with status, origin, task, sandbox, cost. arbe run <id> shows detail.

arbe sessions lists sessions (pure inventory: ID, title, sandbox, age). arbe session <n|id> shows conversation detail. Subcommands: log for the event timeline, history for conversation turns.

Portable runs (authed mode)

When arbe auth is present, runs and sessions are portable across devices. When it isn’t, everything stays local.

No auth: local SQLite only. Nothing leaves the device. opencode’s SQLite holds conversations, arbe’s local store holds run records and session snapshots.

Authed: Postgres holds run records (id, origin, status, timestamps, correlation IDs). Durable streams hold session content. No local SQLite for runs — no dual-write, no sync. Auth is the consent boundary; no per-run opt-in.

The durable stream is the primary session store. opencode’s local SQLite is a working cache. This makes arbe resume work on any device — read from the stream, resume into a fresh opencode session.

Risk: opencode resume-from-external-transcript needs investigation.

Who creates runs and rooms

The API is the single point of authority. Runs can be triggered from any surface — CLI, web UI, DO alarm, webhook. Whoever initiates a run calls the API, which creates the run record and materializes the session room atomically. No surface creates these directly.

Secrets

Two distinct stores:

  • Sprite env vars — infra credentials (Anthropic API key, arbe token). Set once at arbe sprite setup. Only the sprite process reads them. Token rotation is manual — re-run setup. If the token is revoked or a stream write fails, the sprite fails loudly. Silent drops are not acceptable.
  • DO encrypted storage — agent capability secrets (Discord tokens, GitHub keys, etc.). Only the agent DO resolves them at invocation time.

Open questions

Per-sandbox cost. Result.tokenUsage aggregates tokens across sessions in a run. Per-sandbox rollup (total cost across all runs to a given sandbox) doesn’t exist yet — no query surface for that.