# Architecture
> One runtime. Bring your own model. Bring your own triggers. Every action passes through a policy you wrote and a log you own.
The diagram below describes how the seven external surfaces (model providers, triggers, MCP clients, OAuth, dashboard, IDE extensions, external targets) connect to the four internal subsystems of the bridge runtime. Every clause in the [canonical positioning sentence](../README.md) maps to a box on this diagram.
```mermaid
flowchart LR
%% External left side
Models["Model Providers
Claude · GPT · Gemini · Grok · Ollama
Pluggable. Subscriptions or API keys."]
Triggers["Triggers
Cron · File save · Git event · Test run
Webhook · CLI · Phone (Shortcut/PWA)
Anything that can fire."]
Extension["VS Code / JetBrains Extension
LSP · debugger · editor state"]
%% MCP clients
MCPClients["MCP Clients
Claude Code CLI
Claude Desktop
claude.ai · Codex CLI
Mobile PWA"]
%% Bridge runtime — the heart
subgraph Bridge["Patchwork Bridge Runtime"]
direction TB
ToolRegistry["Tool Registry
177+ built-in + plugins
hot reload via --plugin-watch"]
RecipeEngine["Recipe Engine
RecipeOrchestrator · parser · scheduler"]
Policy{{"Delegation Policy
risk tiers · 4-source precedence
(managed → project-local
→ project → user)
+ approval queue"}}
Trace[("Trace Memory
decision_traces.jsonl
RecipeRunLog · ActivityLog
ctxQueryTraces")]
end
%% Right side
Transports["MCP Transports
WebSocket · stdio shim
Streamable HTTP"]
OAuth["OAuth Surface
/.well-known/* · /oauth/*
PKCE S256 · CIMD
Optional. Activate with --issuer-url."]
Dashboard["Dashboard + Mobile PWA
localhost:3200
push approvals"]
External["External Targets
Connectors (Slack, GitHub,
Linear, Gmail, …)
filesystem · terminal"]
%% Inputs
Triggers --> RecipeEngine
MCPClients --> Transports
Transports --> Bridge
OAuth -. gates .-> Transports
%% LLM round-trips
Models <--> Bridge
%% Tool dispatch — every outbound call passes through the policy gate
RecipeEngine --> ToolRegistry
ToolRegistry --> Policy
Policy -- approved --> External
Policy -- needs nod --> Dashboard
Dashboard -- decision --> Policy
%% Trace writes
ToolRegistry --> Trace
Policy --> Trace
RecipeEngine --> Trace
%% Trace reads
Trace -. session-start digest .-> Bridge
%% Extension
Extension <--> Bridge
classDef external fill:#fef3c7,stroke:#a16207,color:#000
classDef gate fill:#fee2e2,stroke:#b91c1c,color:#000
classDef store fill:#dbeafe,stroke:#1e40af,color:#000
class Models,Triggers,MCPClients,Extension,External,Dashboard,OAuth external
class Policy gate
class Trace store
```
## How to read this
The five primitives from the [canonical positioning sentence](../README.md) line up with the four boxes inside the **Patchwork Bridge Runtime** subgraph:
| Sentence clause | Box | Why this box |
|---|---|---|
| *pluggable model providers* | (Model Providers ↔ Bridge) | Bridge is provider-agnostic; arrow direction is bidirectional because LLM round-trips can originate from either side |
| *hot-reloadable tools* | Tool Registry | The `--plugin-watch` flag re-registers atomically on plugin file changes |
| *YAML recipes* | Recipe Engine | RecipeOrchestrator + parser + scheduler |
| *delegation policy with approval queue* | Delegation Policy (gate-shaped) | Every tool dispatch passes through this — it's a structural checkpoint, not a sidecar |
| *durable trace memory* | Trace Memory (cylinder) | Three log files under `~/.patchwork/` plus the activity log under `~/.claude/ide/` |
## Five things to notice
1. **Every outbound action passes through the policy gate.** The arrow from Tool Registry to External Targets does *not* exist as a direct edge — it goes through Delegation Policy first. This is the structural invariant that makes "delegation policy" load-bearing rather than decorative. See [src/approvalHttp.ts](../src/approvalHttp.ts) for the implementation.
2. **Trace Memory is the only multi-source sink.** Tool calls, policy decisions, and recipe runs all write to the same trace store. That's what makes [`patchwork traces export`](../src/commands/tracesExport.ts) a single bundle and what makes the Decision Replay Debugger possible.
3. **Triggers are inputs, not outputs.** Cron, file save, git, test run, webhook, CLI, and the mobile PWA all enter at the same point — the Recipe Engine. The trigger surface is the *non-developer onboarding* story; recipes don't care which trigger fired them.
4. **OAuth is optional and only gates one transport.** The bridge runs without `--issuer-url` and accepts WebSocket / stdio clients on the loopback interface only. OAuth activates the Streamable HTTP transport for remote MCP clients (claude.ai, the mobile PWA). See [src/oauth.ts](../src/oauth.ts).
5. **The dashboard is a policy reader, not a separate brain.** Approval prompts come *from* the Delegation Policy (when a human nod is required) and decisions flow *back* into the Delegation Policy. The dashboard does not have its own approval state; it is a UI over the bridge's queue. See [src/approvalQueue.ts](../src/approvalQueue.ts) and [dashboard/src/app/approvals/](../dashboard/src/app/approvals/).
## What's not in the diagram
Things that exist but are deliberately omitted to keep the page legible:
- **Per-language LSP fallbacks** (TypeScript LS, ctags, etc.) for when the IDE extension is disconnected.
- **Plugin watcher** as a separate component — folded into Tool Registry.
- **Session checkpoint / handoff** — operates orthogonally to this diagram.
- **Connector OAuth flows** (Gmail, Slack, etc.) — separate from the bridge OAuth surface; lives inside the connector implementations.
For the unabridged tour: [documents/data-reference.md](data-reference.md) and [documents/platform-docs.md](platform-docs.md).
---
## Layered view
If the flowchart above is too dense, the same architecture in three layers:
```mermaid
flowchart TB
subgraph L1["1. Where requests come from"]
direction LR
T1[Triggers]
T2[MCP Clients]
T3[IDE Extension]
end
subgraph L2["2. The runtime"]
direction LR
Tools[Tool Registry]
Recipes[Recipe Engine]
Pol{{Delegation Policy}}
Mem[(Trace Memory)]
end
subgraph L3["3. What requests reach"]
direction LR
Models[Model Providers]
Ext[External Targets]
Dash[Dashboard / PWA]
end
L1 --> L2
L2 --> L3
L2 -. policy gate .-> L3
```
The compression is honest: every request enters at layer 1, every effect lands at layer 3, and layer 2 — *the runtime* — is the part that distinguishes Patchwork from each of the alternatives in [comparison.md](comparison.md).