Architecture
A bird's-eye view of how Bigwave's components fit together. Read this once before customising anything.
High-level shape
Bigwave is a single FastAPI process behind a Compose stack:
┌──────────────────┐
│ Web UI │ React/Vite, served at :8080
└────────┬─────────┘
│
▼
┌─────────────────────────────────────────┐
│ apps/api (FastAPI) │ :8000
│ │
│ /v1/chat/completions /v1/embeddings │
│ /v1/models /healthz │
│ /admin/* /webhooks/* │
└────┬──────┬──────┬──────┬──────┬──────┬──┘
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
gateway serving storage eval guardrail channels
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
fallback ollama sqlite/ golden_ noop / telegram /
chain or vllm postgres set, dsl slack
gateEach box on the bottom row is an ABC with multiple in-tree adapters; the operator selects which adapters run via ./install.sh flags. See optional-by-design adapters for the full vocabulary.
Request lifecycle
A /v1/chat/completions request walks the following pipeline:
- Auth —
require_bearer_tokenresolves the bearer to aPrincipal(with workspace + roles);proxy_headersadapter is the alternative when a reverse proxy fronts bigwave. - Rate limit — per-workspace token-bucket (
enforce_workspace_rate_limit); 429 withRetry-Afterif the bucket is dry. - Guardrail (input) — DSL provider checks the last user message against the workspace policy;
Blockshort-circuits with OpenAIfinish_reason="content_filter". - RAG injection (optional) — when
bigwave_kb_idis set, the gateway fetches workspace-scoped chunks from the vector store and prepends them as a system message. - Engine dispatch —
chat_with_fallbackpeeks the first chunk from the primary model. On pre-stream failure it re-tries the next handle in the fallback chain. Mid-stream errors propagate. - Streaming response — SSE chunks land back to the client; the InferenceLog spy captures the assembled response text + token counts for observability.
- Provenance + audit —
CompletionProvenanceCacherecords(completion_id → workspace, model, adapter, prompt template)so the feedback submit endpoint can resolve later. The InferenceLog row persists on stack drain.
Key invariants
- Workspace fence. Every storage repository takes a
workspace_idparameter; cross-tenant fetches return 404 (never 403 — no count-oracle leak). This is enforced at the repository layer, not the route. - Rollback semantics on activation. When
ReplicaManager.activatefails — gate block, drain timeout, engine load failure — the old handle stays live. There is no half-broken state. - Audit append-only.
AuditLogRepository.recordis the only write path; nothing in tree updates or deletes audit rows. - License-check on the runtime path.
scripts/license-check.pyruns in CI. GPL/AGPL on the runtime path fails the build.
Where each piece lives
| Component | Path |
|---|---|
| FastAPI composition root | apps/api/main.py |
| Lifespan wiring | apps/api/lifespan.py |
| Settings / env vars | apps/api/config.py |
| Web UI | apps/web/src/ |
| Gateway routes | packages/gateway/adapters/firstparty/routes.py |
| Serving (engine, ReplicaManager) | packages/serving/ |
| Training (TrainingBackend, registry) | packages/training/ |
| Eval (Scorer, Suite, Runner, Gate) | packages/eval/ |
| Storage backends | packages/shared/db/adapters/ |
| Channels (Telegram, Slack) | packages/channels/ |
| Guardrails (noop, DSL) | packages/guardrails/ |
| Observability (stdout, Phoenix) | packages/shared/observability/ |
| Audit log | packages/shared/audit.py |
| Rate limit | packages/shared/rate_limit.py |
Reading order for newcomers
docs/charter.md— the long-form thesis.docs/architecture.md— system-design depth (you are here, abridged).docs/adr/— the architecture decisions and why we made them.docs/prd/— per-component product requirements.AGENTS.md— repo conventions, non-negotiables, parallel-worktree workflow.
Reading order for production deployment
- Getting started — prove the loop on your laptop first.
- Configuration reference — every knob.
- Docker Compose deploy — production patterns.
- Use cases — match your buyer outcome to a recipe.