System Architecture¶
Turbo EA is a self-hosted web application built from four main components. This page describes the stack, explains the design choices, and covers the optional modules you can add to extend the platform.
The four-component stack¶
Browser (React SPA)
│
│ /api/* (HTTP proxy)
▼
Edge Nginx ──────────────────────────► /drawio/* (self-hosted DrawIO)
│
│ proxy_pass :8000
▼
FastAPI Backend (Python 3.12, uvicorn)
├── SQLAlchemy 2 (async, asyncpg)
├── Alembic migrations
├── JWT auth (HS256) + bcrypt
├── SSE event stream
└── Rate limiting (slowapi)
│
▼
PostgreSQL 18
React SPA (frontend)¶
The entire UI is a single-page application built with React 18, MUI 6, and React Router 7. Vite handles the build; all route-level pages use lazy() imports for code splitting, so only the code for the current page is loaded. The SPA talks exclusively to the FastAPI backend through the /api/ prefix — there is no direct database access from the frontend.
Key frontend libraries: AG Grid (inventory table), Recharts (charts), bpmn-js (BPMN editing), TipTap (rich text), @dnd-kit (drag-and-drop), React Flow (architecture diagrams).
FastAPI backend¶
The backend handles all business logic, data access, permission enforcement, and real-time events. It is structured as a collection of routers (one per domain) that all mount under /api/v1/. Every route handler is async, using SQLAlchemy's async session with the asyncpg driver.
Authentication uses JWT tokens (HS256, stored in the browser's sessionStorage). Bcrypt handles password hashing. Sensitive values stored in the database (SSO secrets, SMTP passwords) are encrypted with Fernet symmetric encryption before writing and decrypted on read.
PostgreSQL¶
All application data lives in PostgreSQL. The schema is managed by Alembic; the application runs alembic upgrade head on every startup, so schema migrations are applied automatically on update. Tables use UUID primary keys throughout.
There is no Redis, no message queue, and no external cache. Real-time updates use Server-Sent Events (SSE) over a long-polling HTTP connection directly from the backend.
Edge Nginx¶
A thin Nginx container sits in front of the React and FastAPI containers. It serves the compiled React SPA, proxies /api/* to the backend (with SSE-appropriate headers), serves the self-hosted DrawIO at /drawio/*, and applies security headers (CSP, HSTS, X-Frame-Options, etc.). In production this is the only container exposed to the network.
Why self-hosted?¶
Enterprise Architecture models contain highly sensitive data: technology budgets, vendor contracts, strategic initiatives, and organisational hierarchies. Many organisations have compliance or data residency requirements that prevent storing this information in a third-party SaaS. Turbo EA is designed to run entirely within your infrastructure — your data never leaves your network.
Optional modules¶
Ollama (AI suggestions)¶
Add --profile ai to Docker Compose to start a bundled Ollama container alongside the main stack. The backend's AI suggestion pipeline calls the Ollama HTTP API to generate card descriptions using a locally-running LLM. The model runs entirely within your infrastructure.
You can also point Turbo EA at any Ollama-compatible external provider (OpenAI, Anthropic via a proxy, etc.) by setting AI_PROVIDER_URL.
MCP server¶
Add --profile mcp to start the Model Context Protocol server. This exposes Turbo EA's data to AI assistants (Claude Desktop, VS Code Copilot, etc.) as a set of tools — read-only queries by default, with opt-in write tools guarded by per-call size limits and a confirmation flow.
The MCP server authenticates via OAuth 2.1 delegated to the Turbo EA SSO provider, so users see the same permissions through MCP tools as they do in the web UI.
Data flow: a card save¶
To make the architecture concrete, here is what happens when a user saves a card:
- React calls
PATCH /api/v1/cards/{id}with the updated payload - Nginx proxies the request to the FastAPI backend
- The route handler validates the JWT, checks the user's
inventory.editpermission, and validates the request body with a Pydantic schema - SQLAlchemy writes the updated row to PostgreSQL
- The calculation engine runs any active formulas for this card type
- The data quality score is recomputed from the fields' weights
- An event is published to the in-memory SSE bus
- Any connected browser receives the event and updates the UI in real time
Ports and networking¶
| Service | Internal port | Host-exposed |
|---|---|---|
| Nginx (edge) | 80 | HOST_PORT (default 8920) |
| FastAPI | 8000 | No — internal only |
| React (dev only) | 5173 | Yes in dev, served via Nginx in prod |
| PostgreSQL | 5432 | No — internal only |
| Ollama (optional) | 11434 | No — internal only |
| MCP server (optional) | 8001 | Via Nginx /mcp/ prefix |
See also¶
- Installation & Setup — how to deploy the stack
- Understanding the Metamodel — the data model design
- MCP Integration — configuring the MCP server
- AI Capabilities — configuring the AI suggestion feature