Turns the design doc into an ordered, dependency-correct build plan (phases 0-9) with an exit criterion per phase, and records the twelve architectural decisions it rests on as ADRs. Decisions: single Rust binary with routed compose services; Streamable HTTP only; per-agent bearer tokens with clientInfo as a display hint only; SQLite; get_rules delivered via session gating; full-document rule delivery; project_id from day one; enforcement tier scoped to Bridle-mediated actions; stable tool list on upstream failure; OpenAI-compatible embeddings with model/dim guarding; pattern RAG gated behind a spike; CLAUDE.md + AGENTS.md as v1 renderer targets. Deviates from the design doc's original 1-8 ordering by moving the audit log and multi-project schema into phase 1, building the admin API incrementally rather than all at the Web Panel phase, downgrading vendor guardrail sourcing to manual-first, and gating the pattern-example RAG behind a validation spike. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RZerbsHGF9Ka3bKhCjJ9m
1.5 KiB
ADR-0001: Single Rust binary with routed compose services
Status: Accepted (2026-08-08)
Context
The design doc's architecture diagram shows Rules, Memory, the Kanboard wrapper, and the Web Panel as separate boxes, which reads as four independent deployables communicating over HTTP. Bridle targets single-host, single-user deployment. Splitting hand-written components across processes at that scale buys nothing: it costs four ports, four health checks, four failure modes, and turns what would be function calls into network hops.
Decision
All hand-written components compile into one Rust binary with internal modules: mcp (server
and session handling), rules, memory, proxy, admin, and the Web Panel's served assets.
Module boundaries stay clean enough that any module could be split into its own service later
without redesign.
Third-party and off-the-shelf services remain separate Docker Compose containers — Qdrant, Kanboard, the embedding server, and the existing Gitea MCP server — with all agent traffic to them routed through the binary.
Consequences
- One deployable, one health check, one log stream, one thing to restart.
- No Python sidecar: the memory module is Rust talking to Qdrant and to an OpenAI-compatible embeddings endpoint over HTTP.
- Compose profiles for bundled-versus-external services are unaffected — see the deployment section of the design doc.
- If Bridle ever outgrows single-host, the module boundaries are the seams to split along. This is a deliberate deferral, not an oversight.