blog · 2026-07-31 · written for: ED-4 — the self-principal verifier-class agent (deterministic answers, nothing narrative); B2A — with the ED-3 security reviewer as the human twin

No bare values: the per-attribute provenance envelope

One thesis: an attribute without its provenance is not data, it is liability — so there is no bare value anywhere in this API; every attribute travels inside an envelope carrying source, dataset version, digest, license class, fetch time, and confidence, and the license class is enforced at assembly time, not disclaimed in a footer.

This post is written for the reader that consumes the contract rather than the prose: the verifier-class agent that needs deterministic, filterable answers, and the security reviewer who audits what that agent is allowed to persist. Both of you ask the same question of any enrichment response: what, exactly, may be done with each field? Here is the machine-readable answer.

The envelope, field by field

{
  "attribute": "servingSize",
  "value": "30 g",
  "source": {
    "id": "usda-fdc",
    "record": "https://fdc.nal.usda.gov/…",
    "dataset_version": "FDC 2026-04 full download",
    "digest": "sha256:…"
  },
  "license": "public-domain",
  "fetched_at": "2026-07-31T18:20:11Z",
  "confidence": 1.0
}
  • source.id — the registered source, one of a closed set with a pinned manifest (below).
  • source.record — the dereferenceable upstream record. Claims you can click are claims you can check.
  • source.dataset_version + source.digest — which snapshot of the source produced this value, pinned by hash. Two responses citing the same version and digest are citing the same fact.
  • license — a closed enum, not free text: public-domain · open-attribution · share-alike-odbl · gs1-licensed-display-only · commercial-restricted.
  • fetched_at — when the value was read; staleness is measurable, not vibes.
  • confidence — 1.0 for a keyed registry hit; lower, and stated, for anything inferred.

Enforced, not descriptive

The load-bearing design decision: the license class is an enforcement input, not metadata. A response is assembled against the caller's declared use, and a response assembled for storage or redistribution cannot contain gs1-licensed-display-only attributes — they are filtered at assembly, not footnoted. share-alike-odbl attributes arrive carrying the attribution block the license requires. For an agent, this converts an unanswerable judgment call ("may I persist this blob?") into a mechanical filter: keep public-domain, carry the notice for share-alike-odbl, decline to persist anything restricted. The permission travels with the value, so your policy engine needs no side-channel knowledge about upstream contracts.

For the reviewer, the same property answers the questionnaire before it is sent: the API cannot hand your storage layer a value your organization has no right to store, because the response shape that reaches storage scope was constructed to exclude it. The one scheme where this machinery bites hardest is deliberate: driver's-license payloads carry a hard no_retention policy flag in the scheme registry — parse and verify run local, and enrich refuses by design, because state statutes criminalize retention. Same enum, same enforcement path, statutory rather than contractual input.

Why per attribute, not per record

Because real records are mosaics. One food GTIN legitimately answers with nutrition from public-domain USDA FoodData Central, category tags from the ODbL Open Facts family, and — where a display-scoped agreement exists — brand-verification fields that may be displayed but never cached. Stamp one license on that record and you must inherit the strictest term across every field, which destroys most of the record's usable value; or you quietly under-claim the restriction, which is how audits are failed. Per-attribute classes keep each fact under its own terms, and keep sources segregated — independent databases cited at answer time, never merged into a monolith. That segregation is also exactly what the ODbL's collective-database posture asks for; the architecture and the license discipline turn out to be the same shape. The flagship chain post shows a full mosaic response on a real kitchen UPC.

Sources are pinned like schemas

Every source in the closed set has a manifest — recorded in PINS.json, printed by the pins verb — carrying the license text's URL, the retrieval date, and the sha256 of the license text itself. This is the same pins idiom the family uses for normative schemas on epcis.dev, applied to legal texts: an upstream license change is a failed pin that stops the build, not a silent drift discovered eighteen months later in someone's diligence. pins gives a verifier the digests to compare; a response's dataset_version and digest tie every value back to a pinned snapshot. Nothing in the chain rests on "trust us" — each link is a hash or a URL.

The failure mode this design refuses

The prevailing alternative in this category is the unattributed blob: hundreds of millions of rows, no per-field lineage, terms that forbid the caching the product is made of. An agent cannot act responsibly on a blob — it cannot answer "may I store this," "how old is this," or "who says so," and so every downstream decision inherits an unquantified risk. An envelope-shaped answer is strictly smaller and strictly more usable: what is absent is honestly absent (an attribute with no licensed source does not appear — it is never invented), and what is present carries its own permission slip.

Stated as the one-line contract, the way an agent should cache it: every attribute is a claim; every claim names its source, its snapshot, its license, and its age; the license is enforced before the response exists. The network half of the verb set — enrich, bridge, hierarchy, party — runs behind keys: get an API key, declare what arrives at your door, and the provisioning order follows the list.

Keys open the network half of the verb set. Get an API key — say what arrives at your door, and the provisioning order follows the list.

We answer in writing. We take at most five conversations a month, only when you ask for one, and only after you already have the written read.