Agent Trust & Assurance · Signed Data Products

The Anatomy of a
Signed Data Product
determinism you can verify

A signature usually promises that nothing changed. This one promises something stronger — that nothing can change: same request, same governed SQL, same rows. A signed data product cannot answer differently, drift, or invent.

Trust & Assurance · visual story · companion architecture: Signed Data Products (OBDA)

Act I

Declared — the product is a contract on the ontology

A data product is not a query someone saved. It is a producer-declared subgraph of the ontology: which entities are exposed, the only joins assembly may ever use — each with its cardinality declared, never inferred — and the measures. The declaration is the manifest; the manifest is the contract.

fitness_sales data_product · the signed contract member Sales fitness_db.Sales key: SaleID Customers fitness_db.Customers key: CustomerID SaleDetails fitness_db.SaleDetails key: SaleDetailID N:1 1:N measures sale_count = COUNT(*) · line_revenue = SUM(Quantity × UnitPrice)

The manifest freezes what the resolver said at authoring time (deployed: — rule M1), declares every join's cardinality explicitly (M3), and names the grain of every entity (M2). Nothing at consumption time is ever guessed.

Why a contract, not a view

The ontology is the substrate

The product lives as a gold node on the knowledge graph, holding member edges to the entities it exposes. Agents meet it while planning — rendered as a consumption contract, not a bare name — and the graph's own governance travels with it.

Rejections are food, not walls

A closed REJECT taxonomy

Every rule a manifest can break has a machine-readable code and a near-miss hint ("unknown measure 'TotalRevenue' — closest declared: line_revenue"). A consumer — human or model — corrects in one step instead of probing.

Act II

Proven — the V1–V7 gate earns the signature

Before a product can be signed, the platform mechanically verifies that every request in its admissible space compiles to deterministic, aggregation-safe SQL. Seven checks; the report states exactly what evidence backed each one; the report itself becomes part of what is signed.

CheckWhat it provesEvidence
V1Every entity resolves to exactly its frozen deployed table — drift means "re-validate + re-sign"schema / structural
V2–V4Join columns exist · the join graph resolves to a unique tree · every expression parses and transpilesstructural
V5The grain-safety matrix: every measure × dimension pair classified safe, or rejected naming the 1:N hop that would multiply rowsstructural
V6The determinism battery — built from the admissible map itself, compiled twice, byte-identicalstructural
V7Live probes: the battery executes at the real target; every declared N:1 and every entity key is spot-checked empirically against the datalive
the naive flat join orders × order_items in ONE join tree EMEA order_count: 2 → 4 each order counted once per line item silently wrong — and confidently so the per-anchor assembly one sub-aggregate per anchor, FULL OUTER JOIN on the dimension tuple EMEA order_count: 2 → 2 an absent slice is an honest NULL, never a dropped row multiplication is impossible by construction

The compiler's core rule, executed as proof in CI: both forms run on a real engine against hand-computed truth — the naive join provably multiplies, the assembly provably cannot.

V7 · caught in the act

The gate does not trust the producer — it asks the data. During acceptance, a product deliberately declared Sales → SaleDetails as N:1, claiming SaleDetails unique on SaleID. The live probe ran the composite-safe uniqueness check against real Teradata and answered: "the data CONTRADICTS the declaration — 2 duplicate group(s) found." Not signable. Sign-off refused.

And the probes police the platform's own tests too: a fixture declaring a column that doesn't exist (Products.Category) failed V7 with the database's own Error 3810 — before anything could sign over it.

held

The probe agreed with the declaration — battery executes, cardinalities and keys verified empirically.

contradicted

The data disagrees with the manifest. V7 fails naming the declaration; the product cannot sign.

structural

No connection available — the gate proves everything it can and the report says mode: structural, honestly. The mode is attested content: it changes the report hash.

Act III

Signed — the proof travels inside the signature

Most signatures attest a name. This one attests content and proof together: the signable hash binds the manifest and the validation report that passed it. Five conditions must hold, each individually refusable, each machine-readable — a signature that cannot be a rubber stamp.

manifest_hash the contract, canonicalised validation_report_hash the V1–V7 proof, evidence mode included Ed25519 the earned seal refuses, by name: invalid_manifest unvalidated stale_report gate_blocked unsigned_ingredient edit the manifest → stale_report · skip the gate → unvalidated · unsigned ontology → the bottom-up rule
The ratchet

Change anything, and the seal says so

The badge's live hash recomputes the same formula from the node's current state. An edited manifest, a refreshed report, a drifted deployment — each reads changed against the attestation, never silently intact. Re-validate, re-sign: the ratchet only turns forward.

Bottom-up, like the agent shield

A signed product contains only signed ingredients

A product signs only on a signed-and-intact ontology — the same compositional rule that governs the agent badge, extended one layer down. And it extends upward too: composed products demand signed members (Act VI).

Act IV

Confined — "cannot query outside its slice", literally

Declare an agent surface-only, and the set of signed products on its surface is its entire data authority. Raw SQL is denied fail-closed at every tool chokepoint. The one exception is precise: the only raw statement it may execute is one the surface itself compiled this turn — edit a single character, and the contract is forfeit.

agent surface-only hand-written SQL → DENIED fail-closed, at every chokepoint "NEXT STEP: call query_product…" — the denial teaches the fix query_product → compiled SQL registered this turn · verbatim only 32 real rows lane: governed

From the live acceptance: the lure turn — three raw-SQL attempts, three denials, zero rows, lane confined — then the natural question: the planner chose query_product first, a wrong measure was rejected with the fix named, one-step self-correction, and the compiled statement returned 32 rows from live Teradata.

The third lane · honest accounting

A denial is neither lane

A denied call never executed — so it counts as confined, not free-formed. The alternative would record the confinement working as the escape it prevented, and score the agent down for being protected. Found live; fixed at the root.

E5 · the monitor of the prevention

If the wall leaks, that is a siren

Prevention subsumes detection — but detection stays on as the audit of prevention. Free-formed SQL executed by a surface-only agent records a danger incident: "a hole report, not agent misbehaviour." It fires only on the breach, never routinely.

Not a misbehaving agent

A steady denial count is a product gap

After the first denial the agent self-corrects to the surface. If denials keep appearing, the consumer needed something the surface doesn't offer — producer backlog, made measurable. The scorer that counts them never gates.

The knowledge twin · K5

Signed knowledge only

The same posture, for the other modality: requireSignedKnowledge restricts retrieval to collections whose integrity signature is signed-and-intact — a changed or unsigned corpus never backs this agent's answers.

Act V

Receipted — every consumption seals a verifiable receipt

When a governed answer ships, the consumption itself is sealed into the turn's Ed25519 provenance chain: the product, the exact request, the compiled statement's hash, the row count, and the trust state at the moment of consumption. The receipt is offline-verifiable — and it is checked against the turn's own record, so the seal and the display can never quietly disagree.

query_intake signed strategic_plan signed obda.consumption fitness_sales · 32 rows trusted · sealed hashes turn_complete signed chain tip each step hash-chained to its predecessor · Ed25519-signed · exportable, verifiable with the public key alone
One function, both sides

The receipt is a closed set of fields — a stray presentation field can never enter the seal — and the same canonical function that seals it is the one offline verification uses to re-derive it from the turn's displayed record. A receipt matching no recorded operation is a named mismatch, never a silent skip. The answer capsule shows the row (fitness_sales · trusted · 32 rows); "Export receipt" carries it inside the signed chain.

Act VI

Composed — signed ∘ signed, with lineage

Products stack. A composed product declares member products and the bridging joins — nothing else — and flattens through the unchanged pipeline: the same gate, the same compiler, the same probes, bridges included. If the flat form passes, it signs without re-judgement: composition preserved determinism, and the gate proved it.

fitness_360 composed · lineage inside the signature fitness_sales signed · attested hash recorded phase_a_acceptance signed · attested hash recorded bridge · N:1 SaleDetails → Products, on ProductID — declared with cardinality, probed live like any other join

Live: fitness_360 signed over its two members, then answered a cross-member query through the bridge — 20 real rows of revenue by product type, governed lane, lineage-bearing hash in the provenance stamp.

P2 · anything ∘ unsigned

Not signable — naming the member

Before its members were signed, the composed sign-off refused: "a composed product contains only SIGNED members — not signed-and-intact: phase_a_acceptance." The agent shield's bottom-up rule, one level down.

P3 · stale-ingredient, structurally free

A parent's re-sign flips its children

The composed badge recomputes its lineage from the members' current attestations — so a re-signed parent reads through as changed on every child. And the cascade is honest: a metadata-only change in the parent re-signs cleanly (the flat contract is byte-identical); a shape change forces re-validation first.

Proof

The acceptance, in numbers

Every figure below is from the live acceptance runs on real Teradata — not projections, not a demo dataset.

12
live probes held on the signed product — battery, cardinalities, keys, verified at the target
2
duplicate groups that unmasked a deliberately false N:1 — the lie could not be signed
3 → 0
raw-SQL attempts by a surface-only agent: three denials, zero rows escaped, lane confined
32
rows from the governed lane after one-step self-correction — compiled, registered, executed verbatim
20
cross-member rows through a composed product's bridge — signed ∘ signed, with lineage
21
Ed25519 chain steps on the receipted turn — verified: 0 mismatches, offline
5
refusal conditions on the signature — each machine-readable, each individually tested
~40
mutations armed against the subsystem's gates — every one fails by name
Why this matters

Detection tells you an answer drifted. Prevention makes drift unreachable. The rest of the trust stack observes, grades, and enforces what an agent says. Signed data products go one layer deeper — they constrain what an agent can even reach, and then hand the reader a sealed, independently verifiable receipt for every number that crossed the boundary. Same request, same governed SQL, same rows. Signed.