Skip to content

Architecture Overview

Yigtwxx edited this page Jul 12, 2026 · 1 revision

Architecture Overview

Maestro is a single-origin web application: a Next.js frontend and a FastAPI backend behind one Caddy reverse proxy, backed by four data stores plus a local model runtime. This page gives the top-down picture; the deep dives are linked throughout.

System topology

flowchart LR
    subgraph Client
      B[Browser<br/>Next.js SPA + SSR]
    end
    subgraph Edge
      C[Caddy 2<br/>single public origin, auto-TLS]
    end
    subgraph App
      F[Frontend<br/>Next.js standalone server]
      A[Backend<br/>FastAPI + Uvicorn]
    end
    subgraph Data
      PG[(PostgreSQL 16<br/>users, billing, task runs)]
      MO[(MongoDB 7<br/>logs, sessions, marketplace)]
      QD[(Qdrant<br/>RAG vectors)]
      RE[(Redis 7<br/>rate limit, event bus)]
      OL[Ollama<br/>embeddings + optional chat]
    end
    B <--> C
    C -->|/, static| F
    C -->|/api/*, /health*, WS| A
    F -->|SSR fetch| A
    A --> PG
    A --> MO
    A --> QD
    A --> RE
    A --> OL
Loading

Only Caddy exposes public ports (80/443). It routes /api/* and /health* (and WebSocket upgrades) to the backend, and everything else to the frontend. This means CORS disappears in production and the container image stays domain-agnostic. See Deployment.

Agent hierarchy

Maestro's core is a layered agent pipeline. Each layer has one job:

flowchart TD
    P[User Prompt] --> O[ORCHESTRATOR<br/>classify domain + complexity, route only]
    O --> M[MAIN AGENT<br/>domain expert: plan subtasks, coordinate team]
    M --> W1[SUBAGENT<br/>atomic task + tools]
    M --> W2[SUBAGENT<br/>atomic task + tools]
    W1 --> RV[REVIEWER<br/>optional: deterministic checks + weighted rubric]
    W2 --> RV
    RV -->|approved| SY[SYNTHESIS<br/>merge outputs, stream to user]
    RV -->|rejected + retry hints| W1
    SY --> P
Loading
  • Orchestrator routes only; it never produces work.
  • Main Agent decomposes the task into a plan of assignments for its fixed domain team, running them in dependency waves.
  • Subagent executes one atomic task, optionally calling tools (web_search, data_fetch, code_execution).
  • Reviewer is optional (reviewer_enabled); it runs deterministic validators then a weighted rubric, bouncing failures back to the subagent up to max_review_iterations.
  • Synthesis merges successful outputs and streams the result token-by-token.

Full detail: Agent-Orchestration.

Task lifecycle

sequenceDiagram
    participant U as User
    participant API as FastAPI (/tasks)
    participant E as Durable Engine
    participant DB as Postgres + Mongo
    participant WS as WebSocket

    U->>API: POST /tasks (prompt, provider, reviewer)
    API->>API: quota check (enforce_can_start_task)
    API->>DB: create task_run + task_session (202 Accepted)
    API-->>U: task_id
    U->>WS: connect /tasks/{id}/stream?token=...
    E->>DB: ROUTE checkpoint (domain, complexity)
    E->>WS: node_update, agent_delta events
    E->>DB: EXECUTE checkpoints (per subagent)
    E->>WS: streamed synthesis (agent_delta)
    E->>DB: FINALIZE (completed / _with_warnings / failed)
    E->>WS: task_completed
Loading

The engine records usage tokens on every terminal path (success, error, timeout, cancel) so quota is always charged. See Billing-and-Quota and Realtime-and-WebSockets.

Data stores and what lives where

Store Role Notes
PostgreSQL Users, API keys, subscriptions, usage ledger, durable task runs/checkpoints/questions Source of truth for auth and quota. See Database-Schema.
MongoDB Agent logs, task sessions, marketplace items/installs/reviews, agent configurations, documents metadata, trace spans Analytics + event source; TTL on some collections.
Qdrant conversation_memories, document_chunks Per-user RAG, EMBEDDING_DIM=768. See RAG-and-Memory.
Redis Rate-limit buckets, cross-worker event bus + control channel Optional in dev (in-process fallback); required for multi-worker.
Ollama Embeddings always; chat only when OLLAMA_CHAT_ENABLED=true The free/local tier.

Tech stack and versions

Backend (requirements.txt): Python 3.11, FastAPI >=0.115,<1.0, Uvicorn[standard], Pydantic >=2.9,<3, SQLAlchemy[asyncio] >=2.0.51,<2.1, asyncpg, Alembic, Motor >=3.6, qdrant-client >=1.12, redis >=5.2, httpx >=0.28.1, tenacity >=9.0, sentry-sdk[fastapi] 2.x, ddgs 9.x (DuckDuckGo search), argon2-cffi, pyjwt >=2.10, cryptography >=44, pyotp, qrcode.

Frontend (package.json): Node 20, Next ^16.2.10, React 19.2.7, Zustand 5.0.14, @sentry/nextjs ^10.65, Tailwind 3.4.17, TypeScript 5.7.3, plus motion / animejs / ogl (WebGL effects), react-markdown + remark-gfm + rehype-slug, class-variance-authority + clsx + tailwind-merge (no runtime component library).

Infra images: postgres 16-alpine, mongo 7, qdrant v1.18.2 (prod pin), redis 7-alpine, ollama latest, caddy 2-alpine.

Design principles worth knowing

  1. Adapter pattern for all external providers. New LLM provider, payment processor, or email sender = a new adapter file; existing code is never modified. See LLM-Providers-and-BYOK.
  2. One image, many hosts. No domain, DSN, or website-id is baked at build time; runtime-only server reads (SITE_URL, SENTRY_DSN, INTERNAL_API_ORIGIN). See Frontend-Reference and Configuration.
  3. Durability over liveness. Task state lives in Postgres checkpoints, not process memory; workers hold short leases and reconcile on startup.
  4. Quota is enforced from one ledger only — Postgres usage_records. MongoDB task sessions are analytics, not billing truth. See Billing-and-Quota.

Clone this wiki locally