Cube
Cube is the semantic layer engine. It sits between Trino and downstream consumers (APIs, BI tools, AI agents) and provides a structured, versioned model of business metrics and dimensions — abstracting raw SQL schema into reusable semantic objects.
Components
| Component | Description |
|---|---|
| Cube API | The API server — exposes REST, GraphQL, and SQL interfaces over the semantic model. Horizontally scalable. |
| CubeStore | Cube's pre-aggregation engine — computes and stores materialized rollups in S3 to accelerate common queries. |
Data Source
Cube queries Trino through the Trino Gateway (trino-gateway, not the coordinator directly) as its primary data source, matching the routing pattern used by Superset. This keeps all Trino query traffic — regardless of which BI/semantic layer issued it — visible in one place (Trino Gateway → History).
The connection is passwordless. There is no static Trino password for the Cube service account, and no corresponding entry in Trino's password.db. Instead, driverFactory authenticates each connection with a Keycloak JWT, mirroring Superset's DB_CONNECTION_MUTATOR:
- Query identity (
user→X-Trino-User) is set to the real end-user's identity, taken from thetrino_userclaim that ontology-backend signs into the HS256 token it issues to Cube. This is what Ranger evaluates policy against. - Connection auth is a Keycloak client-credentials JWT, passed via
custom_auth. Cube fetches this from its own OAuth client (enable_service_account: true) — there is no end-user session token forwarded to Cube today, so this is always the service-account fallback path in practice.
Two behaviors in driverFactory/contextToOrchestratorId exist specifically to keep this token fresh:
- The Keycloak token is never cached — every connection is built with a freshly-fetched token. The round-trip is sub-second relative to query execution time.
contextToOrchestratorIdbuckets the orchestrator ID by a 5-minute time window per user. Cube reuses a driver/connection instance — and whatever token was baked into it — for as long as its orchestratorId stays the same, so a static ID would let a connection (and its token) live indefinitely past real Keycloak expiry. The 5-minute bucket forces a new connection, and therefore a fresh token, well inside the access token's lifespan.
If you change the Trino service account's Keycloak client to a shorter access token lifespan than 5 minutes, reduce the orchestratorId time bucket to match — otherwise the same stale-token failure mode this design avoids can reappear.
Pre-Aggregations
CubeStore workers compute pre-aggregations on a schedule and persist them to a dedicated S3 bucket. Queries matching a pre-aggregation are served from the CubeStore cache rather than hitting Trino — dramatically reducing latency for high-cardinality aggregations.
CUBEJS_SCHEDULED_REFRESH_TIMER is currently set to false, disabling Cube's background pre-aggregation refresh scheduler.
Semantic Model
The cube data model (schemas, measures, dimensions, joins) is served by the ontology-backend at a configurable URL. Cube fetches the model on startup and on version updates. This allows the semantic model to be managed in a Git repository and pushed without redeploying Cube itself.
Cube authenticates to ontology-backend's model endpoints the same way it authenticates to Trino — a Keycloak client-credentials token from its own service account, with no caching.
Authentication
Cube uses Keycloak OIDC with dev mode configurable for local development.
Incoming requests to Cube itself (e.g. from ontology-backend) are validated against a symmetric HS256 secret (CUBEJS_API_SECRET), separate from the Keycloak-based auth Cube uses outbound to Trino and ontology-backend. The incoming token must carry a trino_user claim — this is the identity Ranger sees for the query.
Go Deeper
- Trino — the query engine Cube dispatches SQL to
- Ontology Backend — serves the Cube schema model from a Git-backed store
- Semantic Layer Auth — Ontology, Cube & Trino — full identity flow from BFF through Ontology and Cube to Trino