All docs
docs / How it works
Brio — Architecture
Services, data model, the mission, task and attempt flows, sandboxes.
On this page
- 1. Layers
- 2. Services
- 3. Data model (Drizzle, Postgres)
- 4. Core flows
- 4.1 Mission lifecycle
- 4.2 Task state machine
- 4.3 Attempt execution inside a runner
- 4.4 Merge
- 4.5 Brio as MCP server (inbound)
- 4.7 Local runners (M8, D-86, D-87)
- 4.6 A2A (both directions)
- 5. Sandbox profiles (initial)
- 6. Scaling and reliability
- 7. Observability
1. Layers
flowchart TB
subgraph Surfaces
WEB[Web app<br/>Next.js]
CLI[brio CLI]
MCPS[Brio MCP server]
A2AS[Brio A2A server]
REST[REST API + SDK]
end
subgraph Brain["Orchestrator (apps/api + packages/core)"]
PLAN[Planner]
ROUTE[Router]
SCHED[Scheduler / run state machine]
LEASE[Lease + contract manager]
MAIL[Agent mailbox]
VERIFY[Verifier]
SCORE[Scoreboard]
MEM[Memory graph]
end
subgraph Gov["Governance (packages/governance)"]
BUD[Budgets]
POL[Policies + approvals]
LED[Audit ledger]
VAULT[BYOK vault]
end
subgraph Exec["Execution (apps/runner + packages/adapters)"]
Q[(pg-boss queue)]
RUN[Runner workers]
SBX[Sandbox containers<br/>git worktree per attempt]
LOCAL[Local runner daemon<br/>on user's machine]
end
subgraph Vendors["Connectors"]
API[Model APIs]
CLIAG[Coding CLIs]
HOST[Hosted agent APIs]
A2AA[A2A agents]
MCPT[MCP tools]
BRW[Browser agents]
MEDIA[Media APIs]
end
DB[(Postgres + pgvector)]
Surfaces --> Brain
Brain --> Gov
SCHED --> Q --> RUN --> SBX
Q --> LOCAL
RUN --> Vendors
LOCAL --> CLIAG
Brain --- DB
Gov --- DB
RUN --- DBPrinciple: the brain decides, runners execute, governance can veto anything, events record everything.
2. Services
| Service | Responsibility | Runs on |
|---|---|---|
apps/web |
UI. Talks only to apps/api (REST + SSE). |
Vercel |
apps/api |
Auth, REST, SSE, MCP server, A2A server, orchestrator loop (planner/router/scheduler/verifier calls). Stateless besides DB. | Container (≥2 replicas) |
apps/runner |
Pulls attempt jobs, prepares sandbox, invokes adapter, streams events, uploads artifacts. Horizontal scale. | Container host with Docker (or Firecracker later) |
apps/cli |
User CLI (brio login by device flow, run, plan, board, approve) + brio runner, the local runner daemon from apps/runner/src/local (connects outbound to the API's runner hub over WebSocket and runs the work that is its own, §4.7). |
User machine |
apps/evals |
Runs golden missions against Brio and single-agent baselines; writes results. | CI / cron |
3. Data model (Drizzle, Postgres)
All tables have id (uuid v7), workspace_id, created_at, updated_at unless noted. Soft delete via deleted_at where users can delete.
Tenancy & identity
workspaces(name, plan, settings jsonb)members(user_id, workspace_id, role)a2a_tasks(id, workspace_id, user_id, mission_id, context_id, task jsonb): tasks other agents sent Brio over A2A, each a mission, kept in the SDK's JSON form and refreshed from the mission when read (M7)api_tokens(workspace_id, user_id, name, prefix, token_hash, last_used_at, expires_at, revoked_at): workspace-bound tokens for the CLI, the stdio MCP server and A2A clients; only the sha256 is stored (M7)users,sessions,accounts(Better Auth tables);jwksandoauth_*(clients, resources, client resources, access and refresh tokens, consents, client assertions): Better Auth's OAuth 2.1 provider, which remote MCP clients sign in with (M7)
Connectors & agents
connectors— an installed connector:registry_key(→ connectors.json id),display_name,route(api|cli|sdk|hosted|a2a|mcp|browser|local),credential_ref(vault id),configjsonb,healthjsonb,status.agents— configured worker:connector_id,model,role_prompt,toolsjsonb (MCP servers),limitsjsonb (max tokens, max minutes, max cost),sandbox_profile,tags,price_overridejsonb (the user's own price per M tokens),optionsjsonb (vendor request options).connector_models— the connector's live model list as last fetched:connector_id,model,display_name,context_tokens,max_output_tokens,meta,listed_at.model_prices— public list prices (global, not per workspace):preset_key,model,input_per_m,output_per_m,cached_input_per_m,cache_write_per_m,long_contextjsonb,off_peakjsonb,currency,source(vendor_docs|openrouter|models_endpoint),source_url,fetched_at. Everyattempt.costevent records the price it used.crews— named sets of agents + routing prefs jsonb.
Projects & memory
projects—kind(git|files),repo_url,default_branch,settings.memory_nodes—project_id,kind(decision|contract|file|preference|fact|task|agent|prompt),title,body,embedding vector(1536),source_event_id.memory_edges—from_id,to_id,kind(decided_in|implements|touches|prompted_by|supersedes|depends_on).
Missions
missions—project_id,title,goal,status(draft|planning|awaiting_approval|running|verifying|done|failed|cancelled),budgetjsonb,routingjsonb (learned | static | single with an agent: how tasks are routed),assumptionsjsonb,reportjsonb (planner notes, replans, the final mission report).plans—mission_id,version,graphjsonb (denormalised snapshot),status,approved_by.plans— mission plan versions:version,graph(PlanGraph),status(proposed|approved|rejected|superseded),origin(planner|user),lintjsonb (errors, warnings, automatic fixes, estimate).tasks—mission_id,plan_version(0 for the mission's planning task),key(stable slug),type(taxonomy),title,contractjsonb (schemaTaskContract),depends_onuuid[],mode(single|best_of_n|ensemble|maker_checker|debate|cascade|pipeline),mode_paramsjsonb (the plan's n and pipeline steps; the router'sroutewith mode, roles and rationale; theresulta multi-agent mode chose; once-onlymarks),pinned_agent_id,status(pending|ready|leased|running|verifying|revise|done|failed|blocked|cancelled),budgetjsonb,result_artifact_id,revisions(revise loops used),blocked_reason.attempts—task_id,agent_id,role(maker|checker|candidate|judge|merger),status,sandbox_id,worktree_ref,started_at,ended_at,cost_usd numeric(12,6),tokens_in,tokens_out,exit_reason,transcript_ref,dispatchjsonb (what was run),runner_id,heartbeat_at,cancel_requested_at,checksjsonb (the verifier's checklist and outcomes).artifacts—kind(diff|file|doc|url|image|video|audio|dataset|report),storage_ref,sha256,metajsonb,attempt_id.blobs— content-addressed artifact bodies per workspace (workspace_id,sha256,content_type,size,bytes);artifacts.storage_refisblob:<sha256>. Large binaries move to object storage later without changing the ref scheme.reviews—task_id,attempt_id,reviewer_agent_idorreviewer_user_id,kind(check|review|judge|human),verdict(pass|revise|fail),scoresjsonb,reasonstext,evidencejsonb.contracts— shared interface contracts:project_id,name,version,specjsonb or text (OpenAPI, TS types, schema),owner_task_id,status.leases—task_id,resource_kind(path|glob|symbol|contract|external),resource,mode(exclusive|shared),expires_at(heartbeat-renewed),released_at.messages— agent mailbox:mission_id,from_attempt_id,to_task_idorto_attempt_idorto_user,kind(question|answer|review_request|blocker|note),body,status.integrations— the merge engine's rounds per mission and plan version: the finished code branches merged in plan order (refs),status(queued|running|passed|failed|conflict), the integrationbranchandcommit,conflicts, thechecksrun on the result, and the integration diff artifact.handoffs—from_task_id,to_task_id,packetjsonb (schemaHandoffPacket).
Learning
scores—scope(workspace|global),agent_key(connector+model),task_type,alpha,beta(Beta posterior for pass),elo,avg_cost,p50_latency_s,n,updated_at.ratings— user feedback on attempts.eval_runs— golden-mission eval runs (offline or live): every mission under every routing strategy, the per-strategy summary and the PRD verdict, for the evals dashboard.score_history— one point per score update (pass rate, Elo, n) for the scoreboard's trend lines.
Governance
budgets—scope_kind(workspace|mission|task|agent),scope_id,limit_usd,limit_minutes,spent_usd,soft_pct.policies—kind(data_routing|network|approval|connector_allow),rulejsonb (evaluated bypackages/governance).approvals—subject_kind,subject_id,action,requested_by,status,decided_by,decided_at,reason.ledger— append-only, hash-chained:seq,prev_hash,hash,kind,payloadjsonb. No updates or deletes (enforced by trigger).vault_secrets—ciphertext,dek_wrapped,kek_id,label,last_used_at.
Events (the spine)
events— append-only:mission_id,task_id,attempt_id,seq(per mission),type,actor(system|user:id|agent:id),payloadjsonb,ts. Indexed by (mission_id, seq). The board, scoreboard, ledger and memory ingest all read from here.
Event types (initial): mission.created, plan.proposed, plan.edited, plan.approved, plan.rejected, plan.failed, mission.started, mission.blocked, mission.replanning, task.ready, task.routed, task.blocked, task.done, task.failed, lease.granted, lease.denied, lease.released, attempt.queued, attempt.started, attempt.message, attempt.tool_call, attempt.file_edit, attempt.cost, attempt.finished, check.result, review.verdict, judge.verdict, handoff.created, message.sent, message.answered, chat.message (D-109), contract.changed, budget.warn, budget.stop, approval.requested, approval.decided, merge.started, merge.conflict, merge.done, delivery.started, delivery.done, delivery.failed, mission.done, mission.failed.
4. Core flows
4.1 Mission lifecycle
sequenceDiagram
participant U as User
participant API as Orchestrator
participant P as Planner
participant R as Router
participant G as Governance
participant Q as Queue
participant W as Runner
participant V as Verifier
U->>API: create mission(goal, project, budget)
API->>P: plan(goal, project memory, registry)
P-->>API: task DAG + contracts + estimates
API->>U: plan for approval (or auto by policy)
U->>API: approve (edits)
loop for each ready task
API->>R: route(task, scoreboard, budget, policy)
R-->>API: agents + mode + rationale
API->>G: check budget, data policy, approvals
G-->>API: allow / deny / needs approval
API->>API: acquire leases
API->>Q: enqueue attempt(s)
Q->>W: attempt job
W->>W: sandbox + worktree + brief + handoff packet
W-->>API: stream events (messages, edits, cost)
W-->>API: artifacts
API->>V: verify(task contract, artifacts)
V-->>API: pass / revise / fail (+ evidence)
API->>API: update scores, release leases, write handoff
end
API->>API: merge verified results (merge task if conflicts)
API->>U: mission report4.2 Task state machine
pending → ready (deps done) → leased (leases granted) → running → verifying
verifying → done | revise (→ running with same/next agent, max 2) | failed (→ replan proposal)
any → blocked (lease denied / approval pending / budget stop) → ready when cleared
any → cancelledPersist every transition as an event and in tasks.status. The scheduler is idempotent: it can crash and resume from DB state.
4.3 Attempt execution inside a runner
- Fetch attempt, agent, connector, decrypted credential (short-lived, in memory only).
- Create sandbox from
sandbox_profile(image, CPU, mem, disk, network allowlist, timeout). - Check out project into a fresh worktree on branch
brio/<mission>/<task>/<attempt>. - Write
.brio/brief.md(memory brief) and.brio/handoff.json(incoming packets) and.brio/contract.jsoninto the worktree. - Inject Brio MCP mailbox server config for CLI agents (so they can
ask_agent,report_blocker,get_contract). - Call
adapter.run(input, ctx); translate the adapter's event stream into Brio events. - Enforce limits (tokens, cost, time); kill on breach with
exit_reason. - Collect artifacts: git diff, new files, outputs; upload; compute sha256.
- Tear down sandbox; keep worktree ref for merge.
4.4 Merge
Verified attempts' branches are merged in DAG order into brio/<mission>/integration. On conflict, Brio creates a merge task assigned to the best "merge" agent with both diffs, contracts and handoffs; result goes through verification. Final branch is offered to the user (never pushed automatically; a "push/PR" action requires approval and uses the user's Git credentials).
Delivery (M8, D-81, D-82). When a mission on a repository ends with its work integrated, the transaction that ends it also creates an approvals row and a deliveries row (brio/<title>-<id>, kind pull_request on GitHub, else branch). A person decides it (POST /v1/approvals/:id/decide; API tokens cannot); approved, the delivery is queued for the runner holding the work (brio-deliver for the cloud runner, the hub for a local one), which pushes the integration commit to the repository URL with the project's token in the environment and opens the pull request through the GitHub REST API (BRIO_GITHUB_API_URL). Status and its delivery.* event are one write; both side effects are ledger entries.
4.5 Brio as MCP server (inbound)
Tools exposed: delegate(task, constraints), plan_mission(goal), ask_agent(task_key, question), get_brief(paths|topic), report(status, notes), get_contract(name), request_review(artifact). OAuth 2.1 for remote clients; local stdio for the local runner.
4.7 Local runners (M8, D-86, D-87)
brio runner start opens one WebSocket to /v1/runners/connect and says hello with its token (brio_rnr_…, sha256 stored) and what it found (CLIs, Docker). The API's RunnerHub hands it its own work, pulled from the database on connect, on NOTIFY brio_runner_jobs (whoever creates work pokes the runner) and every few seconds: queued attempts whose dispatch names it as target, their pending command checks, queued merge rounds and approved deliveries of projects whose runnerId is it. Claims are the same atomic updates the cloud runner uses; the hub heartbeats the runner's attempts and forwards cancels. The runner does the work with the cloud runner's own code (runAttemptWork, mergeWork, runCommandCheck) in Docker or, with consent, a host sandbox, and sends events and outcomes back; the hub records them with the same functions (recordAttemptFinish, recordCheck, recordMerge). The runner has no database credentials and never receives vault secrets: its agents are coding CLIs under the person's own sign-in. Placement: agents bound to a runner work only on its projects; file work on such a project goes only to its agents; planners and reviewers may stay in the cloud (the runner supplies the repository's file list).
4.6 A2A (both directions)
- Outbound: an
a2aadapter reads a remote Agent Card, maps its skills to task types, sends tasks, consumes streaming updates and artifacts. - Inbound: Brio publishes
/.well-known/agent-card.jsondescribing skills (plan_and_execute_mission,code_change,research_report, …); incoming A2A tasks become missions in the caller's workspace.
5. Sandbox profiles (initial)
| Profile | Use | Network | Limits |
|---|---|---|---|
code-standard |
coding CLIs | package registries, GitHub, vendor API hosts for that agent | 2 vCPU, 4 GB, 45 min |
code-heavy |
builds, e2e | as above + browsers' download hosts | 4 vCPU, 8 GB, 90 min |
api-only |
model API calls | vendor API host only | 0.5 vCPU, 512 MB, 15 min |
browser |
browser agents | allowlist per task, set by policy | 2 vCPU, 4 GB, 30 min |
media |
media APIs | vendor host | 0.5 vCPU, 1 GB, 30 min |
6. Scaling and reliability
- API stateless; runners scale on queue depth.
- pg-boss jobs have retry with backoff; attempts are idempotent by
attempt_id. - Heartbeats on leases and attempts; reaper releases expired leases and marks dead attempts.
- SSE resumes from
Last-Event-ID= event seq. - Rate limits per connector and per workspace (token bucket in Postgres or Redis later).
7. Observability
- Structured logs (pino) with mission/task/attempt ids; OpenTelemetry traces across API → queue → runner → vendor call.
- Per-vendor dashboards: error rate, latency, cost.
- Every external call logged as an event with redacted payload.