Skip to main content

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

ComponentDescription
Cube APIThe API server — exposes REST, GraphQL, and SQL interfaces over the semantic model. Horizontally scalable.
CubeStoreCube'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 (userX-Trino-User) is set to the real end-user's identity, taken from the trino_user claim 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.
  • contextToOrchestratorId buckets 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.
caution

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