Architecture
Baleia is a control plane: it manages connections, catalogs and engines, runs your SQL on those engines and shows the results. The engines are the data plane. Baleia is not a query proxy, a JDBC/ODBC/Flight SQL server or a federation layer: external clients talk to the engines directly, and joins across engines are the job of a federated engine such as Trino (ADR 0001).
Architectural decisions are recorded in adr/. They are the current truth: where this page or the code disagrees with an ADR, the ADR's "Current compliance" section names the issue that closes the gap.
Components
| Component | What it does |
|---|---|
Web app (web/) | SvelteKit single-page app: workbench, data explorer, connections, content, monitoring, settings. |
API (cmd/api) | Go HTTP server. The OpenAPI spec (api/swagger.yaml) is the contract. Serves the web app, the REST API, the execution WebSocket and /mcp. |
Worker (cmd/worker) | Temporal worker: runs executions, metadata syncs, binding reconciliation, monitoring pollers and other recurring jobs. |
| PostgreSQL | Baleia's own metadata: users, workspaces, connections, catalogs, content, executions, audit. |
| Object storage | Any S3-compatible store (RustFS in the dev stacks) holding each statement's result as an Arrow file. |
| Temporal | Durable workflows for everything long-running. It carries ids and statuses, never result rows. |
| Engines | Where SQL runs: Trino (federated) and the direct engines PostgreSQL, MySQL/MariaDB, SQL Server, Oracle, SQLite, ClickHouse and Arrow Flight SQL (ADR 0006). |
| Catalog managers | Polaris, Nessie, Lakekeeper, Iceberg REST, Unity, Glue and Hive Metastore, reached by Baleia for metadata and bound into Trino as catalogs (connections.md). |
How a query runs
- You press Run (or ⌘/Ctrl+Enter). The web app sends the selection, or the statement at the cursor, with the target: engine, catalog binding and schema.
- The API checks access, records the execution and starts a Temporal workflow.
- The worker splits the script with the engine's dialect and runs the statements in order in one engine session (on Trino, as the signed-in user's Trino identity). It stops at the first error; the rest are marked skipped.
- Each statement's rows are written to object storage as an Arrow file, capped by
BALEIA_RESULT_MAX_ROWSandBALEIA_RESULT_MAX_BYTES. - The WebSocket sends status events only: a snapshot first, then statement and execution status. Rows never travel over it.
- The web app reads the result in pages (
GET …/statements/{position}/results?offset=&limit=). The API seeks the covering Arrow batches in the stored file, so neither side loads the whole result. Exports (CSV, TSV, JSON Lines, Arrow) stream from the same file.
Trino security model
- Three identities.
BALEIA_TRINO_ADMIN_USER(defaultbaleia_admin) runs catalog DDL, reconciliation and Lens maintenance — it is never used to run user SQL.BALEIA_TRINO_SERVICE_USER(defaultbaleia) is the worker's fallback identity. User queries run as the logged-in Baleia user's Trino identity (BALEIA_TRINO_USER_IDENTITY:uuid→u_<uuid>, oremail) sosystem.runtime.queries.userreflects the real user. - The catalog guard is the control. The
baleia-catalog-guardaccess control (shipped by the catalog-store plugin) deniesCREATE/DROP CATALOGandSET SESSION AUTHORIZATIONto non-admin identities at the Trino level. The SQL editor'sIsForbiddenInEditorgate (catalog DDL,EXECUTE IMMEDIATEsmuggling catalog DDL,SET SESSION AUTHORIZATION) is UX and defense in depth, never the security boundary. - Trino is private. The default compose stack does not publish Trino on the host; only the API/worker reach it via
expose. Copydocker-compose.override.yml.exampleto publish it for local debugging. - Unauthenticated admin (network assumption). Baleia's Trino has no authentication, so the admin identity reaches the guard without a verified principal. Compose and the minikube dev values set
baleia.allow-unauthenticated-admin=true, which lets any client that can reach the coordinator connect asBALEIA_TRINO_ADMIN_USERand runCREATE/DROP CATALOG. That is safe only while Trino is reachable from the API and worker alone. The Helm chart defaultstrino.catalogGuard.allowUnauthenticatedAdmintofalse; withfalse, catalog management from Baleia is denied, so enable it only after restricting network access to the Trino service (e.g. a NetworkPolicy). Giving the API a real Trino credential, which removes the need for the flag, is planned. - Read-only mode. A session opened with the read-only credential
baleia.mode=readonlyis denied every write/DDL check by the guard.
See ADR 0005 for the decision record.