Skip to content

Architecture

Odysseus is a single FastAPI application (app.py) that serves both the API and the static frontend, backed by SQLite (via SQLAlchemy) for structured data and a set of optional local services for AI features. It's designed to run as one process (or one container) per user/household, not as a multi-tenant SaaS.

This document is a house-pipeline-convention companion to the existing SECURITY.md, THREAT_MODEL.md, and docs/security-ci.md - those remain the canonical security docs; this file is about component shape, not trust boundaries.

Component diagram

flowchart TB
    subgraph Client["Browser"]
        UI["static/ (vanilla JS SPA)"]
    end

    subgraph App["Odysseus process (app.py, FastAPI/Starlette)"]
        MW["core/middleware.py\n(security headers, CORS)"]
        Auth["core/auth.py\n(sessions, TOTP, API tokens)"]
        Routes["routes/*.py\n(~60 route modules)"]
        Agent["src/agent_loop.py + src/tool_*\n(agent/tool execution)"]
        Services["services/*\n(search, memory, hwfit, docs, shell, stt/tts, youtube)"]
        Companion["companion/*\n(remote pairing)"]
        MCP["mcp_servers/*\n(email, image-gen, memory, rag)"]
    end

    subgraph Data["Local data"]
        SQLite[("data/app.db\nSQLAlchemy models")]
        AuthJSON["data/auth.json"]
        Chroma[("ChromaDB\n(vector store, optional)")]
        FS["data/ (uploads, gallery, logs, backups)"]
    end

    subgraph External["External / host services (all optional, user-configured)"]
        LLM["LLM providers\n(Ollama/OpenAI/Anthropic/local servers)"]
        Search["SearXNG / web search"]
        Mail["IMAP/SMTP"]
        CalDAV["CalDAV servers"]
        Ntfy["ntfy push"]
    end

    UI <--> MW --> Auth --> Routes
    Routes --> Agent
    Routes --> Services
    Routes --> Companion
    Agent --> MCP
    Agent --> Services
    Routes --> SQLite
    Auth --> AuthJSON
    Services --> Chroma
    Services --> FS
    Agent --> LLM
    Services --> Search
    Services --> Mail
    Services --> CalDAV
    Services --> Ntfy

Request sequence: authenticated chat turn

sequenceDiagram
    participant U as Browser
    participant MW as SecurityHeadersMiddleware
    participant A as core/auth.py
    participant R as routes/chat_routes.py
    participant CH as src/chat_handler.py
    participant AG as src/agent_loop.py
    participant LLM as LLM provider
    participant DB as data/app.db

    U->>MW: POST /api/chat (session cookie)
    MW->>A: validate session / CSRF / origin
    A-->>MW: user + owner scope
    MW->>R: request, scoped user
    R->>CH: dispatch chat turn
    CH->>DB: load session history (owner-scoped)
    CH->>AG: build tool-augmented prompt
    AG->>LLM: stream completion (+ tool calls)
    LLM-->>AG: tokens / tool_call events
    AG->>DB: persist assistant message, tool results
    AG-->>R: SSE stream
    R-->>U: streamed response

Key directories

Path Purpose
app.py Application entrypoint: FastAPI app, middleware, static mounting, lifespan startup.
core/ Auth, DB session/models, middleware, exceptions, platform compatibility, atomic file I/O.
routes/ One module per feature area (chat, email, calendar, gallery, research, vault, ...); thin HTTP layer over src//services/.
src/ The bulk of business logic: agent loop and tool implementations, LLM client abstraction (llm_core.py), integrations, RAG/embeddings, security helpers (tool_security.py, url_safety.py, prompt_security.py).
services/ Larger subsystems with their own internal structure: search, memory, hwfit (hardware-aware model fitting), research, shell, stt/tts, youtube.
mcp_servers/ Standalone MCP server implementations Odysseus itself exposes (email, image-gen, memory, rag).
companion/ Remote pairing/companion-device support.
static/ The frontend: vanilla JS (no framework/bundler), organized by feature (static/js/editor/, static/js/compare/, etc.).
data/ Gitignored runtime data: SQLite DB, auth store, uploads, logs, embedding/model caches.
tests/ ~1,300+ pytest files plus a tests/streaming/*.mjs Node harness for frontend streaming/markdown logic.
.github/workflows/ CI: syntax checks, pytest (informational), secret scan (gitleaks), workflow security (actionlint/zizmor), container scan (hadolint/Trivy), dependency review, docker publish.

Notable design choices

  • Single-tenant-per-deployment, multi-user within it. Auth is real (sessions, TOTP, API tokens, owner-scoped queries throughout routes/ and src/), but the threat model assumes a trusted set of users on a private network, not public multi-tenant isolation against malicious end users - see THREAT_MODEL.md.
  • No ORM migrations framework visible at this audit - core/database.py defines SQLAlchemy models directly; scripts/update_database.py and scripts/backfill_model_release_dates.py handle ad hoc schema evolution.
  • Encrypted-at-rest integration credentials. src/secret_storage.py provides encrypt/decrypt/is_encrypted, used by src/integrations.py to store user-supplied third-party API keys (e.g. Vaultwarden) rather than storing them in plaintext.
  • Vanilla JS frontend, no build step for the SPA itself - static/js/ is served directly, organized by feature folder.