Skip to main content
You are viewing: API Specification - Complete technical referenceSee also: User Guide | SDK Reference
  • Component: Agent UI Server - FastAPI backend for the desktop chat application
  • Module: gaia.ui
  • Import: from gaia.ui.server import create_app
  • Source: src/gaia/ui/

Overview

The Agent UI Server is a FastAPI-based backend that powers the GAIA Chat desktop application. It provides REST API endpoints for session management, real-time chat with SSE streaming, document library management, and system health monitoring. Key Design Decisions:
  • SQLite for zero-configuration persistence (WAL mode for concurrent reads)
  • FastAPI for automatic OpenAPI docs and Pydantic validation
  • SSE (Server-Sent Events) for streaming chat responses
  • Hybrid document model — global library with per-session attachment
  • Shared database between CLI and desktop app

Module Structure

Routers (src/gaia/ui/routers/)

As of v0.17.x the HTTP surface is split into focused router modules. Every route is registered under the /api prefix (e.g. the chat router serves POST /api/chat/send); the Mount column below names the surface, not the literal path prefix. Additional routers exist beyond the core set below (connectors, goals, hub, memory, schedules).

Server (server.py)

App Factory

Creates a configured FastAPI application with:
  • Request guard + CORS middleware (see Security Considerations)
  • Database initialization and shared AgentRegistry
  • Router registration (include_router for each module above)
  • Static file serving (for web frontend, when dist/ build exists)
  • Dispatch-queue + document-monitor startup tasks
Default port: 4200

Endpoint Registration

Each router under src/gaia/ui/routers/ is mounted via app.include_router(...). Routers receive the shared ChatDatabase and AgentRegistry through the dependency helpers in dependencies.py. Database objects remain accessible via app.state.db for tests.

Streaming Implementation

Chat streaming uses a thread-based producer/consumer pattern:
  1. A background thread calls AgentSDK.send_stream() synchronously
  2. Chunks are placed into a queue.Queue
  3. The async generator polls the queue and yields SSE events
  4. On completion, the full response is saved to the database

Helper Functions


Database (database.py)

Schema

Four tables with foreign key relationships:

Configuration

  • Foreign keys: Enabled (PRAGMA foreign_keys = ON)
  • Journal mode: WAL (PRAGMA journal_mode = WAL)
  • Thread safety: check_same_thread=False (FastAPI runs across threads)
  • Row factory: sqlite3.Row for dict-like access

Transaction Management

Document Deduplication

Documents are deduplicated by SHA-256 file hash. If a document with the same hash already exists, the existing record is returned with an updated last_accessed_at timestamp.

Document Upload Paths

The server exposes two document upload endpoints, each for a different source of truth: The blob endpoint exists because browser File objects never expose a filesystem path, and since Electron 32 File.path has been removed as well (see issue #728). It streams the upload with a running size check (capped at 20 MB), computes a SHA-256 hash, short-circuits on dedup by hash, and only then promotes the file into the managed directory via an atomic os.replace. On failure, partial and orphan files are cleaned up. When a server-owned document is deleted (DELETE /api/documents/{id}), the on-disk file under $GAIA_HOME/documents/ is also unlinked as a best-effort cleanup. User-owned files from upload-path are left untouched.

Cascade Deletes

  • Deleting a session cascades to its messages and session_documents entries
  • Deleting a document cascades to session_documents entries

Models (models.py)

All 14 Pydantic models:

Dependencies

Required

Optional


Error Handling


Security Considerations

  • CSRF: Every mutating (POST/PUT/PATCH/DELETE) request to /api/* or /v1/* must carry X-Gaia-UI: 1. A page cannot set a custom header cross-origin without a CORS preflight, and preflights are approved only from GAIA’s own origins — so a site the user visits cannot drive the backend. Enforced in UIRequestGuardMiddleware (gaia/ui/security.py), not per route, so a new route is covered on the day it lands
  • Origin: A mutating request whose Origin is neither this server, loopback, nor the live tunnel is refused with 403. Extend with GAIA_UI_ALLOWED_ORIGINS (comma-separated) when fronting the backend with a proxy
  • Host: A request whose Host is a name the server does not know is refused with 400, which blocks DNS rebinding. IP literals and loopback names always pass, so --host 0.0.0.0 LAN access keeps working; add named hosts with GAIA_UI_ALLOWED_HOSTS
  • CORS: An allowlist of localhost dev origins. No tunnel origin is listed: the mobile flow serves the SPA from the tunnel, so those requests are same-origin and CORS never applies. The standalone server (python -m gaia.ui.server) binds to localhost by default
  • No authentication: Designed for single-user local use. Do not expose to untrusted networks
  • File path validation: The upload-path endpoint sanitizes all user-provided paths:
    • Null byte rejection (prevents path injection)
    • Path resolution to absolute canonical paths (eliminates .. traversal)
    • File extension allowlist (only document/code file types accepted)
    • See _sanitize_document_path() in server.py
  • Static file serving: URL paths are validated to stay within the dist/ directory via _sanitize_static_path(), preventing directory traversal
  • SQL injection: Prevented by parameterized queries throughout
  • No telemetry: No data is sent externally