Radicas Docs
Concepts

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" .-> OBJ

Why 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.

On this page