Files
boocode/docs/ARCHITECTURE.md
indifferentketchup afaca9e426 feat: MCP {env:VAR} key substitution + coder model/tool-result fixes + docs refactor (v2.7.9)
- MCP secrets: substituteEnvVars recursively resolves {env:NAME} in mcp.json string values from process.env before Zod (opencode-compatible); unset -> '' + boot warning, and invalid-config log names the unset vars (an empty {env:VAR} in a strict url/command field invalidates the whole config)
- data/mcp.json now untracked (.gitignore flips !data/mcp.json -> !data/mcp.example.json); tracked template data/mcp.example.json carries "{env:CONTEXT7_API_KEY}"; .env.example documents the key (9 mcp-config tests)
- Coder fix: message_complete frame model widened string -> string|null (server+web ws-frames parity); dispatcher publishes model: task.model at all 4 external completion points — a null model otherwise fail-closed in publishFrame and dropped the whole frame incl. status:'complete' (regression test)
- Coder fix: claude-sdk mapUserToolResults maps user-message tool_result blocks -> terminal tool_update events (completed/failed w/ output) so tool snapshots resolve instead of spinning forever
- Composer: AgentComposerBar drops §9b resumed/history/new chip + token readout, loses flex-wrap so the row stays one line; CoderPane gains a per-chat localStorage agent-config cache (restores last model on reopen) + threads model into the timeline/chip
- Docs: root CLAUDE.md slimmed (~190 lines), per-app refs split to apps/{coder,server,web}/CLAUDE.md; new docs/coder-backends.md, docs/project-discovery.md, docs/coding-standards/ (cross-app-contract-parity); ARCHITECTURE.md links the backends doc

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 17:01:03 +00:00

125 lines
4.3 KiB
Markdown

# BooCode architecture
Last updated: 2026-05-25. **Navigation:** `AGENTS.md`. **Deep reference:** `CLAUDE.md`.
## System overview
```mermaid
flowchart TB
subgraph client [Browser]
SPA["apps/web React SPA"]
end
subgraph edge [Edge]
Caddy --> Authelia
end
subgraph host ["Host 100.114.205.53"]
subgraph docker [Docker boocode_net]
BooChat["boocode container<br/>apps/server + built web<br/>:9500"]
BooTerm["booterm container<br/>apps/booterm<br/>:9501"]
PG[("boocode_db<br/>Postgres 16<br/>database: boochat<br/>host :5500")]
CC["codecontext sidecar<br/>:8080 internal"]
end
BooCoder["boocoder.service<br/>apps/coder<br/>:9502"]
Agents["Host CLI agents<br/>opencode goose claude qwen"]
LLM["llama-swap<br/>100.101.41.16:8401"]
end
Authelia --> SPA
SPA -->|"HTTP /api WS /api/ws"| BooChat
SPA -->|"WS /ws/term"| BooTerm
SPA -->|"HTTP /api/coder proxy<br/>WS direct"| BooCoder
BooChat --> PG
BooTerm --> PG
BooCoder --> PG
BooChat -->|"HTTP tools"| CC
BooChat -->|"streamText"| LLM
BooCoder -->|"native inference"| LLM
BooCoder -->|"ACP or PTY spawn"| Agents
Agents --> LLM
```
## Three surfaces, one database
| Surface | Code | Runtime | Primary role |
|---------|------|---------|--------------|
| BooChat | `apps/server` + `apps/web` | Docker | Read-only chat, file tools, MCP client, skills |
| BooTerm | `apps/booterm` + terminal panes in `apps/web` | Docker | tmux + xterm.js PTY panes |
| BooCoder | `apps/coder` + `CoderPane` in `apps/web` | Host systemd | Write tools, task queue, ACP/PTY agent dispatch |
All surfaces share Postgres (`boochat` DB). Cross-surface joins link chats, tasks, and sessions.
## BooChat request path
```mermaid
sequenceDiagram
participant U as User
participant W as apps/web
participant S as apps/server
participant DB as Postgres
participant L as llama-swap
U->>W: POST message
W->>S: /api/sessions/:id/messages
S->>DB: user + assistant streaming rows
S->>S: inference.enqueue()
loop outer step loop
S->>L: streamText
L-->>S: deltas / tool calls
S->>W: WS frames via broker
opt tool calls
S->>S: executeToolPhase
S->>DB: message_parts
end
end
S->>DB: finalize message
S->>W: session_updated user frame
```
Key modules: `services/inference/turn.ts` (outer loop), `stream-phase.ts` (AI SDK adapter), `tool-phase.ts`, `services/broker.ts`, `hooks/useSessionStream.ts`.
## BooCoder execution paths
```mermaid
flowchart LR
Msg["User message<br/>CoderPane"] --> Route{"provider?"}
Route -->|boocode| Inf["In-process inference<br/>pending_changes queue"]
Route -->|external| Task["tasks row<br/>dispatcher poll"]
Task --> ACP["ACP dispatch<br/>opencode goose"]
Task --> PTY["PTY dispatch<br/>claude qwen"]
ACP --> Host["spawn install_path<br/>on host"]
PTY --> Host
Inf --> Apply["apply_pending → disk"]
```
Since v2.1.0, BooCoder runs on the host (not Docker). Agent binaries spawn directly — no SSH tunnel.
See [coder-backends.md](./coder-backends.md) for the full dispatch-backend reference: routing predicates, the warm vs. one-shot lifecycle, agent-session resume, and the provider-discovery pipeline.
## Supporting services
| Service | Reachability | Purpose |
|---------|--------------|---------|
| codecontext | `http://codecontext:8080` from Docker network | Code graph / symbol analysis (Go sidecar) |
| llama-swap | `LLAMA_SWAP_URL` env | Local LLM inference + model props |
| SearXNG | `SEARXNG_URL` (Tailscale Fathom) | `web_search` / `web_fetch` when enabled |
| MCP servers | `/data/mcp.json` config | Optional tools (e.g. Context7), read-only in BooChat |
## Config and data files
| Path | Role |
|------|------|
| `data/AGENTS.md` | Global agent registry (bind-mounted `/data/AGENTS.md`) |
| `data/mcp.json` | MCP server config (opencode-compatible shape) |
| `data/skills/` | On-demand skill library |
| `BOOCHAT.md` / `BOOCODER.md` | Container guidance (mtime-cached into system prompt) |
| `apps/server/src/schema.sql` | Canonical DB schema |
## Deploy topology
- **BooChat + BooTerm + Postgres + codecontext:** `docker compose up --build -d` from `/opt/boocode`
- **BooCoder:** `pnpm -C apps/server build && pnpm -C apps/coder build && sudo systemctl restart boocoder`
- **Ports bind to Tailscale IP** `100.114.205.53`, not `0.0.0.0` — use that IP for host smoke curls