In short

L2 is universal-repositary, the store of record for plant truth in TimescaleDB. It is the only layer that opens the database. L1 posts envelopes to its ingest service, and L3 to L6 read through the query API with service keys, never a database URL. L2 holds readings, assets, topology and context records today, with lots, genealogy, twin state and the write log as direction. L4 may keep derived data of its own, but never a second plant record.

Status
as-built (universal-repositary on main) · contract (envelope ingest, query API, ADR-019 context) · direction (fast-loop tables, Plant Box config pull)
Conceptual layer
② Context
Repo layer
L2 universal-repositary
Source
architecture section 3.6.2, section 4, section 5.7 · ADRs 007, 019
configure & prove
L1-L2-DATA-PLANE.md

L2 is the only layer that opens Timescale for plant truth. L1 posts envelopes; L3–L6 read through HTTP with service keys. L4 may hold derived operational data (PSMPlant Situation Model, traces) in its own store — never a second plant system of record.

LabelMeaning
as-builtuniversal-repositary on main
contractEnvelope ingest + query API + ADR-019 context records
directionPlant-wide industrial graph product (explicit non-goal)

Repo#

RepoJobMust not
universal-repositaryTimescale seven schemas; HTTP ingest; query-api; ops consoleL1 protocol adapters; customer Forge UI; hand L2_DATABASE_URL to L3–L6

Typical ports (local compose): ingest :8090 · query :8091 · console :8092 · admin :8093 · Timescale :5433.


Seven schemas (as-built)#

SchemaHoldsWriters
ingestDedup inbox + auditingest only
telemetrymeasurement / event hypertables, aggregates, evidence archives, asset_stateingest
graphAsset topologyseed / admin
commercialtariffs, bills, bill lines, operating ratesingest + seed
featuresproduction, orders, department graph, context tablesingest + seed
baselinesbaseline modelsseed / admin
ledgerM&V ledger intentsL5 flows via ops/SQL paths — not L1 stream

Detail and retention: consumer docs/EXTENSIVE.md. Configure meters and context into these stores via L1-L2-DATA-PLANE.md.

How facts land#

L2 ingest seven schemas

Ingest path

Seven schemas

ingest

telemetry

graph

commercial

features

baselines

ledger

L1 POST /v1/ingest/records
StampedRecordEnvelope

contracts plus bill_line validated

ingest.l1_processed_inbox
dedupe_key

route_record to schema tables

↩ 200 duplicate inserted false

How to read it.

  1. L1 relay POST /v1/ingest/records with StampedRecordEnvelope.
  2. Validate against contracts → insert ingest.l1_processed_inbox on dedupe_key.
  3. New → demux to schema tables (201); conflict → 200 {inserted: false}.
  4. bill_line requires extraction.validated=true or 422.
  5. Historian backfill: POST /v1/ingest/measurements/backfill forces late=true and stamps lineage — same measurement store.

Build now: one Timescale store (D1, D20). Later: graph path queries if needed (architecture section 4).

View Mermaid source
flowchart TB
    %% house-style: l2-ingest-seven-schemas
    l1["L1 POST /v1/ingest/records<br/>StampedRecordEnvelope"]
    gate{{"contracts plus bill_line validated"}}
    subgraph ingestPath["Ingest path"]
        direction LR
        inbox["ingest.l1_processed_inbox<br/>dedupe_key"] --> demux["route_record to schema tables"]
    end
    subgraph seven["Seven schemas"]
        direction LR
        s1["ingest"]
        s2["telemetry"]
        s3["graph"]
        s4["commercial"]
        s5["features"]
        s6["baselines"]
        s7["ledger"]
    end
    dup(["↩ 200 duplicate inserted false"])
    l1 --> gate --> inbox --> demux --> seven
    gate -.-> dup

    classDef govc fill:#fff4d6,stroke:#c99a2e,color:#000
    classDef agentc fill:#e8f0ff,stroke:#5b7bd5,color:#000
    classDef loopc fill:#eef7ee,stroke:#4f9a4f,color:#000
    class gate govc
    class dup loopc

Fast-loop tables and the Plant Box bundle (direction)#

Nothing here is as-built. Sixteen new tables land in the existing schemas; rows arrive as upserts through records/{type} and the normal L1 cloud path, each stamped with episode_id where one applies and the Plant BoxPlant-side computer for the fast loop (direction; D4) lockfile id. Producer, consumers and proposed contracts per table: ../fast-loop/07-interfaces-and-ownership.md section 3. Keys: ../fast-loop/05-records-deployment-operations.md.

SchemaTablesProducerRetention
telemetryfast_reading (compressed batches)L1 edge-agent batch uploadAt least 13 months (full-year replay)
telemetrybillet_push, forged_partL3 twinAs telemetry
featurespart_link, part_flag, time_bin, ht_basket, twin_stateL3 twinAs features
featuresht_test, rejection_rowL1 (lab export, register intake); match fields from the twinAs features
ledgerwrite_log (hash-chained; the record of every write and refusal)L1 stamped-writerLedger retention; never pruned
ledgerfollow_through, missed_savingsL3 twinLedger retention
baselinesparam_row, param_promotionL3 tuning job via L2 HTTP; approver on the L5 staff consoleVersioned; never deleted
graphpart_aliasL5 staff console via L2 admin APICurrent plus history

Plant Box config read. GET /v1/plants/{plant_id}/plant-box-config?since= (plant-box credential) returns the signed plant_box_config bundle that L2 assembles from parts written by L5 (procedures, switches, roster, budgets) and the L3 tuning job (parameter rows, aliases). The Plant Box pulls; the cloud never connects in. The bundle never carries the writer allow-list or limits; those live in the site packVersioned, owner-reviewed plant configuration including topology. See 07 section 4.


What L3–L6 may ask#

AllowedForbidden
Query HTTP (X-Service-Key + X-Org-Id): measurements, assets, tariffs, contextL2_DATABASE_URL in L3–L6
Constraint / roster / condition context readsTreating L2 as a plant-wide graph product

L3 engines KeyError or empty when required tags are missing — that is fail-closed, not invent.


Hard rules#

RuleWhy
Only L2 opens Timescale for plant truthSpine invariant
Dedupe on dedupe_keyAt-least-once L1 relays
No invent asset_id / timestampsData plane honesty
Ops console ≠ customer UICustomer surface is L6

  • Data plane (configure + prove): L1-L2-DATA-PLANE.md
  • Handoffs: ../../handoff/l2/ — prefer this page for architecture

Deep docs in L2

  1. L1–L2 data planeHow plant systems become closed records in one TimescaleDB, and how later layers read them only over HTTP.as-builtdirection8 min
Page history: last 5 changes
  1. 2026-10-07 docs(technical): rewrite layers/ and L1-L2-DATA-PLANE.md to the architecture 322bf46
  2. 2026-10-03 docs(layers): L1 fast read, writer and sync; L2 tables; L5 relay and budgets; L6 surfaces; data-plane topics 5127af9
  3. 2026-10-03 docs(decisions): renumber live ADRs 001-032 in order, mark withdrawn refs ADR-W###, repoint withdrawn links to archive, note partial supersessions 36c944e
  4. 2026-09-27 docs(architecture): add L2 store architecture 7d1b648
  5. 2026-07-30 docs(repo): reorganize for agent navigation and architecture SSOT 4f1a12c

Diagram

100%

Search the architecture