Skip to content
MCP server built inHow that works

Architecture

An installation consists of nine services behind a single reverse proxy. Only that proxy is reachable; everything else stays on the internal network.

On this page

The shape of it

Path of a query
                       ┌──────────── nginx gateway (:8080) ────────────┐
 browser  ───────────▶ │  /  → console    /api/agent → agent   /auth → KC │
                       └──────────────────────────────────────────────────┘
                                    │                 │
                             Console (React/Vite)     Agent (Fastify/TS)
                                                       │  delegated token (RFC 8693)
                                                       ▼
   Keycloak (OIDC)   PostgreSQL + pgvector   Trino ── Iceberg ── Nessie   MinIO (S3)
   identity          platform state,         SQL engine  tables   catalog  object store
                     vectors
                                                       │
                                              Ollama (local embeddings, CPU)

The services one by one

ServiceRole
gatewaynginx. The only reachable door. Routes by path: console, agent API, Keycloak.
consoleThe web interface, a React/Vite SPA. Signs in over OIDC with PKCE.
agentThe backend (Fastify/TypeScript): tools, gates, ingestion, embeddings, lineage, audit.
keycloakLogin and identity. Issues the tokens every other service verifies.
trinoThe SQL engine. Runs every statement under the caller's exchanged token.
nessieVersioned Iceberg catalog — branching and time travel, Git-style.
minioS3-compatible object storage. Buckets: warehouse for table data, audit for the log.
postgresPlatform state and, through pgvector, the embedding index. Plus the Keycloak and Nessie databases.
ollamaThe local embedding model (nomic-embed-text) behind an OpenAI-compatible endpoint. No cloud key.

Delegated identity

The agent holds no data account of its own. When a request arrives, it exchanges the user's token through Keycloak's standard token exchange (RFC 8693) for a token with audience trino, and issues the query with that. Trino's own grants and the row and column policies decide what comes back.

One issuer, in lockstep

Keycloak sits under /auth and pins the public issuer. Every token carries exactly that one — including the token for Trino, while backchannel calls stay internal.

Why the vector index is only an index

Embeddings live in PostgreSQL — the payload does not. A search only determines there which rows might match; their content is then re-fetched from Trino under the searcher's identity. That costs a second hop, but it keeps governance in one place instead of forcing a second implementation of it inside the search index.

Deployment shapes

  • Docker Compose — everything on one machine, for development and demos.
  • Kubernetes through the umbrella Helm chart infra/helm/flaky-platform — the path for production.

The catalog ships with it

An installation brings its own Iceberg catalog (Nessie on s3://warehouse/). Mounting an existing catalog alongside it is not possible yet.

Applies to: This page describes lavalake 0.16.0. Product and documentation live in separate repositories — when in doubt, what the console shows is authoritative.

Something missing?

If these pages do not answer your question, we will walk through the platform against your own case in half an hour.

Book a demo