The Atlas Donna's documentation, bound to its code
147 documents

The BFF trust boundary

How auth actually works, prose bound to the server code: the global guard, the httpOnly cookie session, and the authed client that refreshes on 401.

1 · System architecture — the BFF trust boundary

The single trust boundary is Donna's SvelteKit server. Server-only code lives under src/lib/server/ and is never imported into client code. Three files carry the whole boundary:

  • src/hooks.server.ts — the global guard. On every request it hydrates event.locals.user by calling GET /api/v1/users/me if the donna_at cookie is present (hooks.server.ts:9-14), then enforces routing: forced password rotation takes precedence (:27-30), (app) routes require a user or redirect to /login?next= (:31-34), and authed users are bounced off (auth) screens (:37-44).
  • src/lib/server/lqClient.tslqFetch() attaches Authorization: Bearer <donna_at>, and on a 401 refreshes once against /api/v1/auth/refresh using donna_rt, rotates both cookies, and retries the original request (lqClient.ts:19-47); if refresh fails it clears the session cookies and returns the original 401. lqStream() is a single-attempt variant for SSE — no refresh, because the preceding page load already ran lqFetch and guaranteed a fresh token (lqClient.ts:54-60).
  • src/lib/server/session.ts — cookies donna_at / donna_rt, set httpOnly, sameSite: 'lax', secure: !dev, with the refresh cookie pinned to an 8-hour TTL mirroring lq-ai's default (session.ts:4-20).

Pages get backend data through SvelteKit load (SSR); they mutate through form actions or small BFF proxy routes (+server.ts). Proxy routes exist for two reasons: to attach auth, and to avoid page/endpoint route collisions — e.g. /prompts/items sits beside the /prompts page, and the /tabular-executions/[id] proxy is a separate top-level group precisely so it doesn't collide with the /tabular/[executionId] page.

flowchart TB
  subgraph client["Browser (no JWT, no direct backend access)"]
    UI["Svelte 5 components · runes"]
  end
  subgraph bff["Donna SvelteKit server — the trust boundary"]
    HOOKS["hooks.server.ts<br/>guard + user hydrate"]
    LOAD["+page.server.ts<br/>load / form actions"]
    PROXY["+server.ts<br/>27 BFF proxy routes"]
    CLIENT["lqClient.ts<br/>lqFetch / lqStream"]
    COOKIES[("httpOnly cookies<br/>donna_at · donna_rt")]
  end
  subgraph backend["vendored lq-ai (NEVER edited)"]
    API["api · FastAPI"]
    GW["gateway"]
    WK["ingest-worker · arq-worker"]
    PG[("postgres")]
    REDIS[("redis")]
    MINIO[("minio / S3")]
  end

  UI -->|"same-origin fetch / SSR"| HOOKS
  UI --> LOAD
  UI --> PROXY
  HOOKS --> CLIENT
  LOAD --> CLIENT
  PROXY --> CLIENT
  CLIENT -->|"Bearer + refresh-on-401"| API
  CLIENT -. reads/writes .-> COOKIES
  API --> PG
  API --> REDIS
  GW --> API
  WK --> PG
  API --> MINIO

The base URL is resolved server-side only, from LQ_API_INTERNAL_URL (src/lib/server/env.ts) — in compose it is http://api:8000 (docker-compose.yml:38); in local dev http://localhost:18000. A SvelteKit form-action POST needs an Origin header (CSRF); production sets ORIGIN on donna-web (docker-compose.yml:35-36) or login returns 403.

Cardinal rule, enforced by structure: Donna consumes lq-ai's contract and never forks it. API types are generated from lq-ai's OpenAPI into src/lib/api/backend.d.ts (10,891 lines) and gateway.d.ts (1,639 lines) via npm run gen:api. The 205 distinct /api/v1/... endpoint shapes the BFF references all derive from there.