Skip to content

Architecture

rluisr edited this page Jul 23, 2026 · 1 revision

Architecture

English | 日本語

How maestro turns an issue into a pull request — components, data flow, and trust boundaries.

Big picture

flowchart TB
  subgraph triggers [Triggers]
    UI[WebUI]
    API[HTTP API]
    Poller[GitHub poller]
  end

  subgraph maestro [maestro process]
    Queue[Run queue]
    Exec[Run execution]
    Registry[Agent registry]
    DB[(SQLite)]
    Gateway[MCP Gateway]
  end

  subgraph anthropic [Anthropic]
    Parent[maestro-orchestrator]
    Child[maestro-implementer]
    Vault[Vault credentials]
    Env[Environment / sandbox]
  end

  subgraph external [External]
    GH[GitHub API + GitHub MCP]
    Linear[Linear MCP]
    Stdio[stdio MCPs via proxy]
  end

  UI --> Queue
  API --> Queue
  Poller --> Queue
  Queue --> Exec
  Exec --> Registry
  Exec --> DB
  Exec --> Parent
  Parent --> Child
  Parent --> Vault
  Child --> Env
  Vault --> GH
  Vault --> Linear
  Vault --> Gateway
  Gateway --> Stdio
  Parent -->|create_sub_issue / create_final_pr| GH
Loading

Vertical slices

Code lives under src/features/* (use cases) and src/shared/* (cross-cutting). Entry wiring is src/index.ts.

Feature Role
run-api REST + SSE for runs
run-queue Serialize / schedule run execution
run-execution Orchestration pipeline for one run
decomposition Parent tool: create sub-issues
finalize-pr Parent tool: open the final PR
dashboard Hono SSR WebUI
github-trigger Poll labeled issues / @bot run comments
github-review-webhook Optional PR review hooks
mcp-gateway Bearer auth → loopback mcp-proxy
dev-tunnel Local ngrok path for MCP
stale-run-reaper Fail/cancel wedged running rows
auth Optional OIDC for dashboard/API
ingress-proxy Split app vs MCP hostnames
preflight GitHub + Anthropic access checks
repo-chat Repo-scoped chat (WebUI)

Agents

Name Role Topology
maestro-orchestrator Parent / coordinator Multi-agent coordinator
maestro-implementer Child implementer Roster entry on the parent

Registry (src/shared/agents/registry.ts):

  1. Ensure child agent exists (create/update)
  2. Embed child {id, version} in parent multiagent.coordinator roster
  3. Persist ids/versions in DB table agent_registry_state

Parent custom tools (only these two — no spawn_child_task):

Tool Purpose
create_sub_issue Record a decomposed unit of work on GitHub
create_final_pr Open the consolidated pull request

Child work is delegated through Managed Agents threads, not a custom spawn tool. Observable event names include:

  • session.thread_created
  • agent.thread_message_sent / agent.thread_message_received
  • session.thread_status_*

Run lifecycle

sequenceDiagram
  participant T as Trigger
  participant Q as Run queue
  participant E as Run execution
  participant A as Anthropic session
  participant G as GitHub

  T->>Q: enqueue run
  Q->>E: start
  E->>G: preflight + read parent issue
  E->>A: ensure agents, vault, environment
  E->>A: sessions.create + first turn
  A->>A: orchestrator plans / decomposes
  A->>G: create_sub_issue (custom tool)
  A->>A: child threads implement
  A->>G: push branches via GitHub MCP / git
  A->>G: create_final_pr
  E->>Q: terminal status + events
Loading

Typical phases (visible in WebUI / run events): resolve origin → preflight → vault/MCP credentials → session → decomposition → implementation threads → finalize PR → complete / fail / abort.

Origins

origin Input Notes
github_issue (default) issue number or GitHub issue URL; optional repo URL can supply both repo and number
linear_issue linearIssue identifier/URL + required repo Needs enabled Linear MCP (https://mcp.linear.app/mcp)

Same pipeline after origin resolution: work still lands as GitHub branches + PR on the target repo.

Triggers

Trigger How
WebUI Form on dashboard
POST /api/runs JSON body (see Configuration → API)
GitHub poller Repos in polled_repositories (WebUI Repositories). Label default agent-run; comment @bot run

Poller config env (cadence / keywords only — not the repo list):

  • GITHUB_TRIGGER_POLL_INTERVAL_SECONDS (default 60)
  • GITHUB_TRIGGER_LABEL (default agent-run)
  • GITHUB_BOT_MENTION (default bot)

Persistence

Store Path / table Contents
SQLite default .maestro/dashboard.db (container: /data/app/dashboard.db) runs, events, prompts, MCP servers, polled repos, registry state, …
Runtime files .maestro/ lock, state JSON (gitignored)

DB is source of truth for operator-managed data: prompts, polled repositories, MCP servers, repo prompts/environments/chat, agent registry. Schema lives in src/shared/persistence/db.ts (SCHEMA_SQL).

SQLite = single writer. For HA, prefer one writer + optional replica tooling (e.g. Litestream in the image) owned by ops.

MCP path

Managed Agents only speak remote Streamable HTTP MCP. stdio servers never attach directly.

Managed Agent
  → HTTPS Remote MCP URL
    → (edge / CIDR allowlist)
      → MCP Gateway (Bearer MCP_GATEWAY_TOKEN)
        → mcp-proxy loopback
          → stdio process (mcp-proxy.json)

Builtin GitHub MCP:

  • name: github
  • URL: https://api.githubcopilot.com/mcp/
  • Auth: per-repo GitHub App installation token in Vault (static_bearer), not a shared PAT

Credential model:

  1. Agent definition lists mcp_servers + mcp_toolset
  2. Vault stores credentials keyed by mcp_server_url
  3. sessions.create passes vault_ids only — Anthropic matches URL → credential

Details: MCP-Servers

Cloud vs self-hosted environments

Mode How Code execution
Cloud (default) maestro creates/uses Anthropic environments Anthropic sandbox; optional github_repository mount
Self-hosted ANTHROPIC_ENVIRONMENT_ID on the app Your poller/worker; bootstrap clones under /workspace/maestro-runs/<sha256-run-id>/...

Self-hosted specifics:

  • App never holds ANTHROPIC_ENVIRONMENT_KEY
  • Fixed shared VAULT_ID is disallowed; per-run managed vaults hold MCP credentials only
  • Worker needs git, bash, curl, jq, openssl, GNU timeout, and GitHub App PEM for credential helper
  • Full contract: docs/self-hosted-sandboxes.md

Trust boundaries

┌─ Trusted (your infra) ─────────────────────────────┐
│  maestro app, SQLite, GitHub App private key,       │
│  MCP_GATEWAY_TOKEN, OIDC secrets, worker keys       │
└────────────────────────────────────────────────────┘
          │ API + vault_ids
          ▼
┌─ Anthropic Managed Agents ─────────────────────────┐
│  orchestrator/implementer sessions, vault secrets,  │
│  cloud sandbox (or your self-hosted worker)         │
└────────────────────────────────────────────────────┘
          │ GitHub MCP / git / tools
          ▼
┌─ GitHub / Linear / Figma / … ──────────────────────┐

Rules of thumb:

  • Never put long-lived cloud SA keys or GitHub PEMs in prompts or client-side code
  • Prefer repo-scoped App installation
  • On multi-repo GitHub App installs, avoid one shared VAULT_ID — MCP credentials are URL-keyed and installation tokens can overwrite each other
  • Gateway token is a secret; rotate deliberately and keep stable per environment (churn creates orphan vault credentials)

Observability

  • Run events — durable stream in SQLite; WebUI live tail + GET /api/runs/:id/events (SSE)
  • Audit middleware — HTTP / session / tool structured audit
  • OpenTelemetry — off by default; OTEL_ENABLED=true (+ OTLP endpoint). Sensitive fields are redacted (no prompts, tokens, bodies)

Related code map

Concern Start here
Process boot src/index.ts
Config schema src/shared/config.ts
Agent definitions src/shared/agents/
Parent tools src/parent-tools.ts
Vault / MCP creds src/shared/vault.ts
Constants (URLs, names) src/shared/constants.ts

Next: Configuration · MCP-Servers

Clone this wiki locally