Overview
Orca is built on a two-tier architecture that separates concerns cleanly:Conductor
The Conductor is a stateless Go binary that owns the HTTP API surface and routes work to runners.Responsibilities
- Accept REST requests from the dashboard and API clients
- Maintain a run registry: durable, tenant-scoped Postgres storage (
RunsRepo) backsGET /api/runslisting, pagination, and counts, while an in-memory registry additionally tracks ephemeral plan and workflow node runs that have norunstable row - Delegate session creation to
remote.Pool(round-robin to capable runner) - Fan run events to SSE subscribers with replay and heartbeat
- Manage the MCP server catalog
- Seed default profiles to runners on startup
Key Properties
Because conductors are stateless, you can run any number of replicas behind a load balancer. Session routing never requires shared state: the session ID itself encodes which runner owns it.
sess-<runnerHash>-<8hex>
The runner hash is SHA256(RUNNER_BASE_URL)[:8]. Any conductor can extract the hash from a session ID and route directly to the owning runner without a shared registry.
Runner
The Runner is a stateful Go binary that owns all live agent state.Responsibilities
- Store profiles and sessions in memory
- Build session-scoped toolkit views (intersection of profile
toolslist and registry) - Resolve
${VAR}placeholders in MCP headers from its environment - Dispatch run envelopes to agent-worker over the inbound sidecar API or a session-scoped outbound worker channel
- Expose a session-scoped MCP streamable HTTP endpoint per session
- Advertise capabilities and URL hash to the conductor
Session Lifecycle
agent-worker
The agent-worker is a Node.js process that wraps LLM provider SDKs and streamsRunEvent objects back to the runner. It supports two HTTP transport modes without changing the runtime handlers or run envelope:
Outbound mode is intended for workers inside per-session sandboxes that the runner cannot safely reach. Every runner call carries the session-scoped bearer token. Dropping the event upload cancels the active run, while transient poll or upload failures are retried with bounded exponential backoff.
After each outbound run, and when the runner sends
export_state, the worker best-effort checkpoints conversation state to sandbox disk and uploads a durable copy to the runner. On restart it restores the disk copy before its first poll. The state directory defaults to /var/orca/state and can be changed with ORCA_STATE_DIR.
Mode Dispatch
The sidecar’s behavior is controlled entirely by theMODE environment variable:
MODE defaults to pi when unset.
Runtime Configuration Isolation
The worker runtime is configured from the run envelope, not from the host agent CLI settings. Profile prompts, attached skills, session MCP, external MCP servers, and allowed tools are injected programmatically by Orca.clauderuns the Claude Agent SDK with filesystem setting sources disabled, so host~/.claudeskills, plugins, and project settings are not discovered. Host built-in tools such asBash,Read,Write,Edit,WebFetch, and subagent spawning are disabled by default, so executable work must route through the runner MCP sandbox tools.codexruns with an isolatedCODEX_HOMEand disables project-doc loading, so host~/.codexconfig and repoAGENTS.mdfiles are not inherited by agent runs. Codex runs inread-onlysandbox mode by default; setAGENT_ORC_CODEX_HOMEonly when operators need to move that isolated Codex home directory.
process.env. Model auth and the egress-guard variables are preserved, but worker-only storage credentials, database URLs, Redis URLs, and internal signing keys are not exposed to model-generated shell commands.
Data Flow: End-to-End
1
Client submits a run
POST /api/runs { profile, title, prompt } arrives at the conductor.2
Conductor routes to a runner
remote.Pool round-robins across runners that advertise the required runtime capability. If the request omitted sessionId, the runner creates a fresh session and the conductor returns { runId, sessionId }.3
Client opens SSE stream
GET /api/runs/{runId}/stream: conductor registers subscriber and begins forwarding events.4
Conductor dispatches to runner
The conductor sends the task to the runner that owns the session.
5
Runner dispatches to worker
In inbound mode, the runner dispatches the task to the worker. In outbound mode, the session worker receives the same envelope by long-polling the runner. Profiles that opt in to
@memory first have a --- CONTEXT FROM MEMORY --- block prepended to subtask.Prompt: see Memory Bank. The worker then connects to the LLM provider and begins streaming responses.6
Tool calls loop through runner
When the agent calls a tool, the worker uses the session-scoped tool interface. The runner executes the tool and returns the result.
7
Events flow back
The inbound response or outbound event upload carries
RunEvent NDJSON from the worker to the runner, then the conductor, then SSE subscribers.Capability Routing
Runners advertise their capabilities at startup viaRUNNER_CAPABILITIES (default: pi,claude,codex,vercel). general is not part of the default set; it survives only as a deprecated alias for vercel. The conductor’s remote.Pool only routes sessions to runners that support the requested profile.runtime.
Design Principles
Stateless control plane
Stateless control plane
Conductors hold no session state. The run registry is durable Postgres storage, with an in-memory layer for ephemeral plan and workflow node runs. This makes horizontal scaling trivial: just add replicas.
Session ID as location hint
Session ID as location hint
The runner hash embedded in the session ID means any conductor can route any session request to the correct runner with a single map lookup. No Redis, no shared registry.
Sidecar abstraction
Sidecar abstraction
The runner communicates with the sidecar over HTTP. This means:
- Different LLM providers require no changes to the Go runtime
- Stubs replace real sidecars in tests
- Future providers are new Node.js files, not Go changes
MCP as tool abstraction
MCP as tool abstraction
Every session exposes an MCP endpoint. This unifies platform tools, external APIs, and future sandbox services behind a single interface, regardless of the LLM provider or SDK.
Profile immutability at runtime
Profile immutability at runtime
Updating a profile definition never affects running sessions. Sessions are created from a profile snapshot; they outlive profile edits safely.