Architecture
How the system fits together: broker, protocol, identity, and what Scout owns versus observes.
On This Page
This document is the system-level map for OpenScout: what the system is made of, and how it treats data. If you are new, read it as a guide to four things: what the broker is, what the runtime does, what the protocol defines, and which records Scout owns versus observes.
Read this after the repo README.md↗ if you are orienting to the project for the first time. If you want the command-first ramp first, read quickstart.md before this page. For the precise meaning of Scout's core nouns and how they map onto external agent protocols, read concepts.md. For the question, work-item, and delegation semantics between agents, read agents-and-collaboration.md. If you are evaluating maturity, trust, or license posture, read current-posture.md.
Working Thesis
Scout is a local-first control plane for orchestrating AI agents across harnesses, machines, and interfaces.
Remember these three things:
- Runtime / broker (
packages/runtime): accepts commands, routes messages, and writes Scout-owned coordination records. - Protocol (
packages/protocol): the shared language for agent identities, records, and requests. - Agent sessions (
packages/agent-sessions): normalizes harness-owned sessions into observed events, snapshots, approvals, and topology hints.
Scout does not replace Claude Code, Codex, or any other agent tool. It handles discovery, addressing, observation, and coordination around those tools, regardless of which harness runs them or which machine they live on. A harness is the agent runner and transport wrapper for a specific tool. The agent itself may live outside Scout; what Scout owns is the local routing, binding, session, and durable coordination state around that agent.
In practice, the architecture is aiming for three stable outcomes:
packages/runtime: clients and adapters submit commands to the local broker instead of writing Scout-owned records directlypackages/protocol: one shared model for messages, invocations, flights, identities, and collaboration recordspackages/agent-sessions: many operator surfaces and harness adapters can observe sessions around that core
Platform Scout is distinct from the conversational assistant handle
@scoutbot. The product, broker, CLI, protocol, and coordination model remain
Scout/OpenScout. @scoutbot is the routeable assistant identity that may appear
in operator chrome, mentions, chips, and broker logs when the human is talking to
the assistant. Friendly UI may call that assistant Scout, but use @scoutbot
where the handle matters.
That framing matters because most of the design choices below are about protecting those boundaries.
Principles
A small set of constraints shape every design decision.
Local-first, not cloud-first. The broker, agent registry, and Scout-owned state live on your machine. Nothing phones home by default. Local files and databases are the source of truth, not a hosted API.
High-trust local pilot, not hardened enterprise perimeter. Scout assumes trusted local users, trusted local agents, and explicit pairing/mesh choices; it is not yet a multi-tenant, compliance-ready system. See current-posture.md.
Observe, don't absorb. Harnesses own their primary transcripts and logs. Scout observes them through adapters and tail views, then stores links, metadata, and Scout-owned coordination records without importing external turns wholesale. This boundary is central enough to have its own section below; see The Data Model.
Multi-harness. Agents run wherever they naturally run, with or without Scout. Scout observes and coordinates across those harnesses without assuming one execution backend.
Multi-machine. Agents on different machines discover and message each other through mesh forwarding. Pair a phone or a second workstation and the agent graph extends with it.
File-based configuration. Agent definitions, overrides, and project bindings are JSON files on disk. No dashboard required.
Protocol over product. The protocol package defines the shared grammar. Products are built on top of it, not beside it.
Communication Flow
Agent sessions connect to a single Scout broker over HTTP and SSE. The broker owns the agent registry and routes messages between sessions. Each harness uses its own transport -- stream-JSON for Claude Code, app-server for Codex -- but the protocol layer above is shared.
A typical exchange looks like this:
operator → scout send --to codex "review the auth module"
scout cli → broker /v1/deliver (target intent + message body)
broker → resolves codex → endpoint
broker → codex session (deliver message via SSE)
codex → broker (post reply)
broker → operator (deliver reply via SSE)
One concrete example: scout ask --project ../talkie --harness claude "review the auth module" sends a capability request to the broker. The broker resolves the project/harness constraint to an existing or newly created worker, forwards the request to a compatible session, records the target acknowledgement, and tracks the later completion as a flight. A flight is the broker's tracked record for an ask-style request, including retry, acknowledgement, and completion state. Caller wait budgets may stop a CLI or MCP call from waiting, but they do not cancel or fail the broker flight.
Core Moving Parts
| Layer | Role | Key detail |
|---|---|---|
| Protocol | Shared type system and address grammar | Defines the agent identity grammar, message records, invocation requests, flight records, collaboration contracts, and bindings |
| Broker | Local message bus and state store | SQLite-backed daemon that owns registration, routing, threading, dispatch, HTTP reads/writes, and SSE updates |
| Native read projection | Low-latency local surface reads | Rust journal consumer in scoutd that serves bounded, persisted projections over a Unix socket without entering the broker or web request queues |
| Runtime | Session and runtime lifecycle management | Starts, resumes, stops, and health-checks sessions across harnesses. Manages tmux sessions, system prompts, and transport adapters |
| CLI | Operator interface | scout up, scout send, scout ask, scout who -- passes structured route intent to the broker and keeps bootstrap/orientation cheap |
| Surfaces | Views into broker state | Desktop, web, iOS, terminal, and pi. They read from the broker; none of them own agent state |
Protocol
The shared grammar everything speaks. It defines agent identity (the address grammar in Agent Identity And Addressing below), message records, invocation requests, flight tracking, collaboration contracts, and bindings.
Anything that crosses a boundary — between agents, harnesses, or machines — is described here.
Broker
A single local daemon per machine. Agents and surfaces submit commands to it; it resolves structured targets, routes to endpoints, and records coordination history. They do not write Scout-owned control-plane records directly. It exposes HTTP/SSE for commands, routing, and broker-specific reads. Latency-sensitive presentation reads may consume replayable broker facts through a separate derived projection; that projection never becomes a writer or routing authority.
# What the broker handles
scout send --to hudson "check the deploy" # → resolve, route, deliver
scout who # → read agent registry
scout watch # → SSE stream of all eventsBroker module map
The broker implementation lives in packages/runtime/src/. broker-daemon.ts is the
process composition root: it wires dependencies, starts TCP/Unix/WebSocket listeners,
and runs background loops. Business workflows live in broker-* service modules;
HTTP route dispatch lives in broker-http-router.ts.
broker-daemon.ts composition root (~1.3k lines)
├─ broker-process-manager.ts / broker-server-lifecycle.ts
├─ broker-durable-store.ts + broker-durable-record-store.ts
├─ broker-core-service.ts (read facade + deliver/invoke delegates)
├─ broker-http-router.ts (HTTP route table)
└─ broker-* services (write paths, mesh, streams, sessions)| Layer | Modules | Responsibility |
|---|---|---|
| Process | broker-daemon.ts, broker-process-manager.ts, broker-server-lifecycle.ts | env/config, singleton probe, listen/shutdown |
| Persistence | broker-journal.ts, broker-durable-store.ts, broker-durable-record-store.ts, broker-delivery-store.ts, broker-work-item-store.ts, broker-read-cursor-store.ts | journal append, write queue, entity helpers, deliveries, work items, read cursors |
| Read model | broker-core-service.ts, broker-core-message-read-model.ts, broker-home-service.ts, broker-capability-matrix-service.ts, broker-api.ts | snapshots, feeds, capability matrix, in-process client |
| Routing & dispatch | broker-delivery-routing.ts, broker-unavailable-target-service.ts, broker-delivery-acceptance-service.ts, broker-invocation-dispatch-service.ts, broker-local-endpoint-resolver.ts, broker-local-invocation-service.ts, broker-local-invocation-helpers.ts, broker-flight-lifecycle-service.ts | target resolution, /v1/deliver, invocation accept/dispatch, endpoint selection, local execution, flight lifecycle |
| Conversations & messages | broker-conversation-service.ts, broker-conversation-helpers.ts, broker-message-service.ts, broker-command-service.ts | actors/conversations, message posting, control commands |
| Mesh | broker-mesh-bundle-service.ts, broker-mesh-forwarding-service.ts, broker-mesh-discovery-service.ts, broker-mesh-http-service.ts | peer bundles, authority forwarding, discovery, receiver routes |
| HTTP & streams | broker-http-router.ts, broker-http-helpers.ts, broker-http-entity-write-routes.ts, broker-delivery-http-service.ts, broker-durable-action-http-service.ts, broker-managed-session-http-service.ts, broker-a2a-service.ts, broker-control-stream-service.ts, broker-trpc-router.ts | route table, inbox/delivery HTTP, durable actions, managed sessions, A2A JSON-RPC, SSE, WebSocket firehose |
| Sessions & sync | broker-managed-session-service.ts, broker-managed-session-helpers.ts, broker-local-agent-sync-service.ts | pairing/local-session attach, registry sync |
| Operator surfaces | broker-web-control-service.ts, broker-operator-attention-service.ts, broker-repo-tail-service.ts | web child supervision, operator attention/mobile alerts, repo watch/tail reads |
Shared runtime primitives (broker.ts in-memory registry, scout-dispatcher.ts label
resolution, local-agents.ts harness transports) sit beside this map and are called
from the services above.
Runtime
Manages agent sessions across harnesses — starting them, stopping them, health-checking them. Handles system prompt generation, tmux session management, and transport adapters for each harness type.
Also owns the file-based agent override registry, project discovery, and harness profile resolution.
Local Service Process Tree
On macOS, local service ownership is intentionally layered:
launchd -> scoutd -> scout-base -> scout-broker -> scout-web / scout-edge / OpenScoutMenu
\-> scoutd probes/native-read (Unix socket)launchd keeps scoutd alive. scoutd is the native daemon and doctor at the
root of the Scout-owned runtime tree. Its Rust child also maintains bounded,
read-only projections from the broker journal for native surfaces. scout-base is the Bun service composer:
it starts and restarts broker, web, edge, and menu children. This distinction is
why the Rust binary is named scoutd, while the Bun orchestrator keeps the
scout-base process name.
CLI
The operator's main interface. It sends route intent such as --to hudson or --channel triage to the broker and renders broker receipts, remediation actions, and orientation views. Target metadata belongs in structured command fields, not in the message body.
Surfaces
Desktop host, web dashboard, iOS companion, terminal UI, and pi. These are views into broker-owned control-plane state and observed harness activity. None of them own agent state; the broker does.
The pi extension (see eng/sco-015-pi-scout-integration.md↗) runs Scout coordination as a native pi extension, letting pi sessions send and receive messages via the broker alongside other harnesses.
Performance Direction
Operator commands and surfaces need to stay cheap. They should not repeatedly scan the machine, serialize the full broker registry, or queue presentation reads behind routing work.
The broker is the canonical writer and communications fast lane. Its hot path owns identity, reachability, routing metadata, messages, invocations, deliveries, and flights. Rich cards, UI ordering, and other presentation joins are derived read models rather than synchronous broker work.
The macOS native agent roster is the first native read projection. A Rust service
tails the append-only broker journal asynchronously, keeps a typed bounded projection
in memory, persists the last usable projection, and publishes sequenced NDJSON frames
over a mode-0600 Unix-domain socket. Opening the HUD reads that already-warm state;
it does not require the web app or a broker snapshot request. The web endpoint remains
a compatibility fallback while other surfaces migrate.
The read service may lag briefly and must be stale-while-revalidate. It cannot write Scout-owned records, synchronously proxy the broker, inspect private in-memory broker state, or make routing wait for projection work.
The Data Model
Scout is a control plane, not a transcript warehouse. Its storage model starts from one boundary: Scout owns the records it creates or routes, and observes external harness records without importing them wholesale. This matters for product scope, operator trust, and system design. The broker should make agent coordination durable without pretending to become the canonical database for every model turn written by Claude Code, Codex, or another harness.
Scout's coordination vocabulary is small. A conversation groups related turns; a message is a durable "say this" record; an invocation is a request for work; a flight is the lifecycle record attached to an invocation; a delivery is one routed attempt to reach a target; a binding maps a project path and branch to an addressable target; a question asks for an answer; and a work item owns a durable piece of execution. concepts.md defines each precisely and maps them onto external protocols. This section is about who owns them.
What Scout Owns
Scout owns first-party control-plane records:
- nodes, actors, agents, endpoints, and bindings registered with the broker
- conversations, messages, forwards, and replies created through Scout
- invocations, flights, deliveries, delivery attempts, and dispatch records
- collaboration records such as questions and work items when created through Scout
- local read models and activity projections derived from those first-party records
These records are broker-owned facts. They can be persisted, replayed, projected into SQLite, forwarded across mesh peers, and shown consistently across CLI, desktop, mobile, and agent tools.
What Scout Observes
Scout observes external harness source material:
- Claude Code transcript JSONL
- Claude Code team, task, subagent, and session topology when available
- Codex session JSONL
- Codex subagent, thread, and custom-agent topology when available
- harness-specific logs, turn streams, and file-backed history
- process and filesystem signals that help explain what is running now
These sources are not Scout-owned conversation state. Scout may discover them, tail them, summarize them, link to them, index lightweight metadata, or expose live views over them. Scout should not bulk-copy external transcript turns into its control-plane database and treat them as first-party messages.
For harness-owned ecosystems such as Claude Code's .claude files, the boundary is stricter than "do not bulk-import": adapters should not write there at all. They may inspect what the harness exposes locally, but creating or modifying harness agents, teams, task lists, or MCP settings is outside adapter runtime behavior. Any host setup that changes a harness configuration must be an explicit operator action, not something an adapter does while observing or driving a session.
Storage Split
The intentional split is:
- the broker journal records Scout-owned control-plane facts
- SQLite stores query projections of Scout-owned facts for local surfaces
- Rust native read services may maintain bounded, persisted projections by replaying the broker journal; these caches are disposable and never become canonical facts
- tail adapters read external harness transcripts from their original files
- tail views keep bounded live/backlog buffers rather than becoming durable transcript replicas
If a surface needs raw harness detail, it should prefer the original harness material through an adapter, cursor, or link. If a workflow needs durable coordination, it should create a Scout-owned message, invocation, flight, delivery, or work item.
Design Rules
- Do not make Scout the canonical store for external harness transcripts.
- Do not persist every observed harness turn as a Scout
message. - Do persist Scout-originated and Scout-routed coordination records.
- Do use lightweight metadata, cursors, summaries, and links when external source material needs to appear in Scout surfaces.
- Do not let adapters mutate harness-owned ecosystems such as Claude Code's
.claudestate. - Do keep the boundary visible in docs and APIs:
messagemeans a Scout conversation record, whileTailEventmeans an observed harness event.
This is an intentional product and architecture boundary, not an implementation shortcut. Better indexing and replay tools should still preserve the distinction between owned coordination state and observed harness source material.
Agent Identity And Addressing
This is how Scout turns a human-friendly handle like @hudson into one exact Scout address for routing, and it is the reference for the address grammar.
Every agent Scout can address has a name. When there is only one matching agent on one machine, the name is simple — @arc or @hudson. But agents multiply. The same project might run on two machines. The same workspace might have a main branch and a feature branch, each with its own agent. A project might use Claude for one task and Codex for another.
The identity grammar exists to keep every target unambiguously addressable while still letting humans type the shortest useful name. The default target is the base project/workspace identity. Harness, model, node, and session details describe the concrete instance Scout routes to; they are not a different base agent unless the caller intentionally chooses a specialized profile.
Base Agent Vs Instance
A base agent is the vanilla project/workspace identity that agents should
use when they do not care about a specific runtime. In practice this is the
thing represented by a project path, such as ../talkie, or by a short handle,
such as @talkie.
An agent instance is the concrete attachment Scout routes to for that base
identity: a Claude or Codex harness, a model choice, a machine/node, and
optionally an explicit session id. Asking for a specific instance should refine
the route, not create the impression that talkie#codex and talkie#claude
are separate base agents.
Default rule: if the project is known but the exact agent/session is not, use project routing and let Scout pick or create the concrete instance. Add a harness/capability constraint when that matters:
scout ask --project ../talkie --harness claude "Review this."ask({ projectPath: "../talkie", harness: "claude", body: "Review this." })Do not guess generic names such as claude.main just because you need a Claude
review. Scout should return durable follow-up handles and, when possible, a
situated target handle for the routed worker. Promote that worker to a named
long-lived sibling only after the route is known good.
A situated Scout target is the saved result of that resolution: agent
profile, project, harness, rules/tool context, and the current continuation
handle that Scout should use. Humans type it as target:<name>. Agent-authored
prompts and compact UI may render the same handle as ⌖name. @missionwriter
names a role or definition, #ops names a channel, and
session:<harness>:<native-id> names one exact runtime session. A target handle
can resolve through current broker records to the right ref, session, or
binding, but it is not itself a raw session id.
A route alias is separate broker-owned pointer state attached after creation
to one durable agent id or one exact broker-known session. alias:review
selects it explicitly; a bare review consults it only after native agent
names/selectors fail. Aliases are scoped by owner realm, canonical project, and
authority node. Repointing changes future resolution only: acceptance pins the
binding id/revision and canonical target into durable receipts and records.
Route aliases never create cards, agents, actors, or sessions.
When routing by an agent card, label, or exact agent id, Scout treats the target
as a fresh-session request. Use session:<id> or MCP targetSessionId only
when the caller intentionally wants to continue one concrete prior harness
session. The id may be a Scout id or a harness-native id already known to the
broker. Use session:<harness>:<native-id> or execution.harness when a native
id needs scope. Historical session records and reachability diagnostics are for
that explicit session path, not fallback candidates for normal card routing.
Specialized profiles may become first-class over time. For example,
@scout.profile:investigator could name a profile with a dedicated tool set
and instructions. That is a specialization layered onto the project identity,
not the default routing model.
Runtime launch profiles are a separate concept from the profile: identity
qualifier. Leading reserved names Fable, Kimi, Grok, and Opus in CLI
natural-language asks produce a structured runtime_profile route for a fresh
current-project session. The broker owns the harness/model/default mapping.
Fable and Opus accept broker-validated effort overrides. Kimi and Grok reject
effort overrides until their ACP transports expose a corresponding control.
Likewise, agent Composer Review to ... produces an existing_handle route
for exact @composer-review lookup. It does not create a session, choose a
local duplicate, or consult fuzzy aliases. See
runtime-profile-and-existing-handle-routing.md↗.
Neither route is a post-hoc alias: runtime profiles are launch presets and existing handles name already-known live targets. Mutable route aliases retain their own scoped broker records, revisions, and explicit alias target kind; unknown profile or exact-handle routes never fall through to alias lookup.
Runtime Specification Is Not Identity
RuntimeSpec is a launch contract, never an AgentIdentity. Its canonical,
shell-safe grammar has fixed positions:
[/[/]] Examples are codex, claude/opus, and
codex/gpt-5.6-sol/xhigh. Sparse harness-plus-effort requests use separate
flags because the grammar has no placeholder segment. The equivalent explicit
form is:
scout ask --project ../talkie --harness codex \
--model gpt-5.6-sol --effort xhigh "Review this."Runtime profiles are base presets; valid explicit dimensions override the
preset. The per-dimension launch ladder is explicit flag or RuntimeSpec,
profile preset, endpoint metadata, harness config, then harness default.
Conflicting overlapping selectors and illegal harness/model/effort tuples fail
closed. Exact runtime selection creates an isolated session instead of
restamping or reusing a mutable live endpoint. An exact session:<id> target
is legal only when observed runtime evidence matches every requested dimension.
Invocation receipts preserve an executionResolution record for harness,
model, and reasoning effort. Each dimension distinguishes requested,
resolved plus its source (flag, literal, profile, endpoint, config,
or default), and harness-reported observed truth plus drift. Resolved launch
arguments are not treated as observation.
Bare natural-language tokens use one stable priority: a reserved profile id is
a profile launch; otherwise a launchable harness id is a harness-only
RuntimeSpec; model-family words are never valid bare; remaining names are agent
targets only when they are not reserved. The # harness and ? model
qualifiers retain their fixed meanings on agent identities, and effort is not
an identity dimension.
New agent names and aliases cannot use runtime grammar words (launchable
harnesses, profiles, effort values, route words, dimension keys, product
identities, or built-in definition ids). Exact model ids are intentionally not
globally reserved. Existing offenders remain migration-readable by qualified
id only in the one-time development migration. Normal startup now fails with
reserved_name_existing when a stored project or registry entry still uses a
reserved name; Scout does not silently rename production identities.
Three Layers
Scout separates identity into three layers, each serving a different audience:
-
Canonical identity is exact, stable, and system-owned. It includes every dimension needed to distinguish one agent from all others. Humans rarely type it, but the broker always stores it.
-
Minimal unique identity is the shortest address that still resolves to exactly one agent. Scout computes it automatically from the current set of online agents. When there is only one
hudson,@hudsonis enough. When two exist on different machines,@hudson.node:minidisambiguates. -
Configured identity alias is an input declared on an agent definition and participates in native identity resolution.
-
Route alias is mutable broker state over an already-existing agent or exact session. It has its own binding id, revision, owner/project/node scope, authorization, expiry, and audit history. It does not mutate identity.
The Six Dimensions
An agent identity combines up to six dimensions:
| Dimension | What it captures | Example |
|---|---|---|
definitionId | The base project or workspace | arc, hudson |
workspaceQualifier | A non-default worktree or branch | super-refactor, main |
profile | Optional specialization/persona, not the default route | dev, investigator |
harness | Instance execution backend | claude, codex |
model | Instance model family or concrete model | sonnet, gpt-5-5 |
node | Instance machine or host | mini, macbook |
The canonical form strings them together with dots:
@<definitionId>[.<workspaceQualifier>][.profile:<profile>][.harness:<harness>][.model:<model>][.node:<node>]In practice, most of these dimensions are omitted. You only include what is needed to resolve unambiguously.
Examples
From shortest to most qualified:
| Address | What it resolves |
|---|---|
@arc | The only arc agent currently online |
@arc.main | The arc agent on the main branch |
@arc.super-refactor | The arc agent on a feature worktree |
@arc.main.harness:claude | The Claude instance of arc on main |
@lattices#codex?5.5 | Compatibility shorthand for the Codex instance of lattices on a 5.5 model |
@lattices#claude?sonnet | Compatibility shorthand for the Claude instance of lattices on Sonnet |
@arc.super-refactor.harness:claude.node:mini | Fully qualified: project, branch, harness, machine |
Parsing And Normalization
@is required for body-mention compatibility in user-facing text. Internal systems can omit it, and Scout-aware composers may use the>>route operator instead, such as/scout:ask >> hudson Review this.- One positional qualifier (without a type prefix) is allowed after the definition ID — it is always treated as the workspace qualifier.
- Typed qualifiers (
profile:,harness:,model:,node:) may appear in any order during input. Scout normalizes them to canonical order on storage. - Shorthand
#<harness>maps toharness:<harness>, and?<model>maps tomodel:<model>. - Segments are lowercased and kebab-cased:
Super Refactorbecomessuper-refactor,Mini.localbecomesmini-local. Dots are reserved as separators.
These aliases are accepted during parsing and map to canonical dimensions:
| Alias | Maps to |
|---|---|
branch:, worktree: | workspaceQualifier |
persona: | profile |
runtime: | harness |
#codex | harness:codex |
?sonnet | model:sonnet |
host: | node |
Resolution
When you type @hudson, Scout resolves it against all known agents:
- Check for an exact alias match first.
- Match the parsed identity against registered candidates.
- If exactly one candidate matches, resolve to it.
- If zero or more than one match, return nothing — the name is either unknown or ambiguous.
This keeps short names fast and ergonomic while requiring precision only when the situation demands it.
Minimal Unique Identity
Given a specific agent and its peers, Scout prefers the shortest address that uniquely identifies it. Dimensions are dropped in this order until removing the next one would create ambiguity:
workspaceQualifierprofileharnessmodelnode
If a configured alias is shorter than the minimal canonical form and resolves uniquely, Scout prefers it.
Example:
- Canonical:
@hudson.hudson-main-8012ac.node:arachs-mac-mini-local - Minimal unique:
@hudson(if only one hudson is online) - Alias:
@huddy
The Integration Boundary
Plugging an agent into Scout means three things at the data level: register an identity, expose a reachable endpoint, and attach a session.
- Identity is the stable Scout address for the agent, built from the dimensions above (
definitionId,workspaceQualifier,harness,model,node). Human text uses a short handle; the broker resolves it to one exact target. - Endpoint is how the broker reaches the agent: an agent id, an authority node, a harness, a transport, a session reference, and current reachability. The endpoint is a route, not the agent's personality — an agent can move between sessions or machines while keeping a stable identity.
- Session is the concrete harness conversation, process, or thread that receives work. Sessions are harness-specific: a request for a Codex harness must not bind to a Claude session, or the reverse, without an explicit adapter.
Once attached, an integration speaks two paths through the broker:
- the message path (
send) for durable "say this" updates that return a broker receipt but no tracked work lifecycle - the invocation path (
ask) for work, where the ask creates an invocation, the invocation creates a flight, and the flight tracks queued, running, waiting, completed, failed, or cancelled state
Two rules hold across both paths. Do not hide routing instructions in the message body when structured target fields exist — the broker should know the target as metadata, not by parsing prose. And never write harness-owned files: adapters observe .claude and Codex state, they do not author or repair it.
That is the shape of the boundary. For the full adapter contract — endpoint states, session invariants, broker-guided routing, preferred MCP tools, human-input and permission handling, and the compatibility checklist — read agent-integration-contract.md. For the wire-level message and invocation workflows, read scout-comms.md↗.
Addressing And Session Lifecycle
-
Bind. Create or refresh a Scout-local binding from a project path and branch to an addressable agent target.
-
Start or attach.
scout uplaunches or resumes the harness session Scout should use for that target, with a generated system prompt that includes the collaboration contract when Scout owns the launch path. -
Route. Messages with explicit target intent hit the broker, which resolves the name, finds the endpoint, and dispatches.
-
Invoke. For ask-style interactions, the broker creates a flight record -- tracking the request-response lifecycle with acknowledgement, retry, and durable completion semantics.
-
Stop.
scout downterminates the local session and marks the endpoint offline.
scout up hudson # bind + start
scout send --to hudson "hi" # route + deliver
scout ask --project ../talkie --harness claude "..." # capability route + invoke
scout ask --to hudson "..." # route to a known agent + invoke (tracks flight)
scout down hudson # stopMesh
Agents on different machines discover each other through mesh forwarding. Each broker advertises its local agents to peer brokers, which sync endpoint tables so @agent.other-machine resolves across the network. In Scout, mesh means reachability and coordination, not global consensus, exactly-once delivery, or replicated external transcript storage.
Discovery
Brokers find peers two ways: by probing Tailscale's peer list (tailscale status --json) and through manually configured seed URLs. No mDNS, no cloud discovery service. If you're on a Tailscale network, your brokers can find each other automatically. If not, point them at each other with OPENSCOUT_MESH_SEEDS.
# Automatic — brokers on the same tailnet discover each other
scout mesh discover
# Manual — seed a broker URL directly
OPENSCOUT_MESH_SEEDS=http://workstation-2:43110 scout mesh discoverOnce a peer is found, the broker fetches its agent registry via /v1/snapshot and merges remote agents into its local database. Each agent carries an authorityNodeId — the node that owns it. Messages and invocations for remote agents get forwarded to the authority broker over HTTP.
Forwarding
When you scout send --to hudson "..." and hudson lives on another machine, the broker's delivery planner detects that hudson's authorityNodeId differs from the local node. Instead of delivering locally, it bundles the message with its full context — actors, agents, conversation, bindings — and POSTs it to the remote broker's /v1/mesh/messages endpoint. The remote broker commits the bundle to its own journal and delivers locally.
Invocations work the same way. Ask-style requests forward to the authority node, which executes them and returns the flight record.
Pairing
A phone or second workstation joins the mesh through scout pair. The local broker starts a relay (or connects to an external one), generates a QR code with a pairing payload, and waits. The remote device scans the code, connects over a Noise-protocol-encrypted channel, and becomes a full mesh peer — not just a viewer. It gets a live view of the agent graph and can send messages into any conversation.
scout pair # show QR code, start managed relay
scout pair --relay url # use an external relayWhat Scout Is Not
Scout is not a framework for building agents. It is not a cloud service. And it is not a replacement for any single agent tool. It is the connective tissue between agent runtimes, harnesses, and operator surfaces.