The three layers
Storage engine, cost canonical schema, and operational telemetry standard — three separate decisions that Radicas never conflates.
Radicas is built on three distinct layers. Each answers a different question, each is a separate decision, and they are never conflated. Once you hold the split in mind, the rest of the product — reconciliation, allocation, per-feature cost — falls into place.
Layer 1 — storage engine: where data lives
Data lives in ClickHouse for hot analytical queries, with a bronze layer of raw records on S3-compatible object storage (Parquet). Every tenant shares the same engine and the same tables, isolated by tenant identity (see tenancy and ingestion). The storage engine says nothing about how cost or agent runtime is modelled — it is only where the other two layers persist.
Layer 2 — cost canonical schema: how cost is modelled
Cost is modelled as a billing ledger conforming to FOCUS 1.3 (the FinOps Open Cost and Usage Specification), extended with x_* columns such as x_span_id and x_tenant_id. Rows come from provider invoices and cost reports, and from rate cards for seat-based plans. FOCUS is deliberately a ledger: authoritative about money, and not sufficient on its own for agent operations — that is layer 3's job.
Layer 3 — operational telemetry standard: how agent runtime is modelled
Agent runtime is modelled with the OpenTelemetry gen_ai semantic conventions: the span tree of an agent invocation (invoke_agent, generate_content, execute_tool), the model requested, token usage, tool calls, and Radicas attributes such as radicas.feature. This layer is operational truth in real time — and the source of estimated cost.
Two schemas, one engine
Layers 2 and 3 land in the same engine (layer 1) as two distinct schemas — FOCUS gold tables for cost, OTel gen_ai tables for telemetry. They are joined per span (see estimated vs authoritative) but never blended into one schema.
flowchart TB
subgraph L3["Layer 3 — operational telemetry standard"]
OTEL["OTel gen_ai semantic conventions<br/>span tree · models · token usage · tool calls"]
end
subgraph L2["Layer 2 — cost canonical schema"]
FOCUS["FOCUS 1.3 billing ledger + x_* extensions<br/>invoice line items · rate cards"]
end
subgraph L1["Layer 1 — storage engine"]
CH[("ClickHouse — hot analytics")]
OBJ[("Object storage — bronze, Parquet")]
end
OTEL -- "gen_ai tables" --> CH
FOCUS -- "FOCUS gold tables" --> CH
CH -. "raw / tiered" .-> OBJWhy the separation matters
The layers change for different reasons: storage evolves for scale and cost, the billing ledger evolves with the FOCUS specification and provider invoices, telemetry evolves with the OTel gen_ai conventions. Keeping them apart means every new telemetry or billing source is just another ingestion path normalizing into the same two schemas — no per-source storage, no per-source schema, ever.
Concepts
The four ideas behind the Radicas data model — three distinct layers, estimated vs authoritative cost, tenancy, and allocation.
Estimated vs authoritative cost
Two standards, one engine — real-time estimated cost from telemetry, authoritative cost from invoices, reconciled span by span, and the allocated basis that never reconciles.