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
┌──────────── 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
| Service | Role |
|---|---|
| gateway | nginx. The only reachable door. Routes by path: console, agent API, Keycloak. |
| console | The web interface, a React/Vite SPA. Signs in over OIDC with PKCE. |
| agent | The backend (Fastify/TypeScript): tools, gates, ingestion, embeddings, lineage, audit. |
| keycloak | Login and identity. Issues the tokens every other service verifies. |
| trino | The SQL engine. Runs every statement under the caller's exchanged token. |
| nessie | Versioned Iceberg catalog — branching and time travel, Git-style. |
| minio | S3-compatible object storage. Buckets: warehouse for table data, audit for the log. |
| postgres | Platform state and, through pgvector, the embedding index. Plus the Keycloak and Nessie databases. |
| ollama | The 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