Skip to content
GitLab

Output & Serving

Declares the shape of data this product produces. This is the contract with downstream consumers. Breaking changes require a major version bump.

apiVersion: akili/v1
kind: Output
schema:
- name: event_id
type: uuid
primary_key: true
role: identity
description: Unique event identifier
- name: user_id
type: string
nullable: false
role: event_key
description: User who triggered the event
- name: event_type
type: string
nullable: false
description: Type of user interaction
- name: event_timestamp
type: timestamp
nullable: false
description: When the event occurred
- name: page_url
type: string
nullable: true
description: URL of the page where the event happened
- name: session_duration_ms
type: integer
nullable: true
role: measure
description: Duration of the session in milliseconds
format: parquet
partitioning:
- field: event_timestamp
granularity: day

Supported types:

TypeNotes
stringVariable length
integer64-bit signed
floatDouble precision
decimal(p,s)Precision and scale required
boolean
dateCalendar date
timestampAlways stored with timezone (UTC)
uuid
jsonStored as string in Iceberg
array(T)Nested type, e.g. array(string)
map(K,V)Key-value, e.g. map(string,integer)

Column roles:

RoleMeaningConstraints
identityNatural/business keyAt least one per schema. Immutable across versions.
attributeDescriptive propertyDefault if role omitted
measureNumeric fact for aggregationMust be numeric type
event_keyForeign key to another product’s identityMust reference valid upstream identity

Caution: Every output schema must have at least one column with role: identity. Identity columns cannot be removed, renamed, or have their type changed in any version — they are immutable. To change identity structure, create a new product.

Schema evolution rules:

ChangeVersion Impact
Add nullable columnMinor bump
Remove columnMajor bump
Rename columnMajor bump (treated as remove + add)
Widen type (int to bigint)Minor bump
Narrow/incompatible type changeMajor bump

Declares where and how consumers access the output. You state intent; the platform routes to the correct store.

apiVersion: akili/v1
kind: Serving
endpoints:
- type: lookup
description: Point lookups by event_id for the Portal
config:
index_columns:
- event_id
- user_id
- type: analytics
description: Event analytics for dashboards
- type: realtime
description: Latest events per user, refreshed continuously
config:
key_template: "user:{user_id}:latest"
ttl: 24h
include_columns:
- user_id
- event_type
- event_timestamp

Store routing:

You DeclarePlatform Routes ToWhen To Use
type: lookupPostgreSQL (upsert, RLS)Point lookups, Portal API, key-value access
type: timeseriesTimescaleDBTime-range queries, trend analysis
type: analyticsStarRocks (federates to Iceberg)OLAP, GROUP BY, dashboards
type: realtimeRedis (SET with TTL)Sub-10ms reads, streaming hot path
(no endpoints)S3/Iceberg onlyCold storage, time travel, batch

Note: S3 (Ceph RGW) via Iceberg is always written to, regardless of endpoint configuration. It is the source of truth with full time-travel capability. All other stores are optimized projections.

Type-specific config:

TypeKey ConfigNotes
lookupindex_columnsColumns to index in PostgreSQL. Defaults to primary key.
timeseriestime_column, chunk_intervalColumn for hypertable partitioning.
analyticsNonePlatform registers the Iceberg table in StarRocks. No data movement.
realtimekey_template (required), ttl, include_columnsUse column_name placeholders in key template (e.g. user:$col:latest).

A product can declare multiple serving types to serve the same data through different access patterns simultaneously.