§3 Lineage (ADR-0005)
3.1 The problem
VER 1.0 records what a pipeline did to an Asset (provenance.chain) and what a signer asserted about it (C2PA), but nothing about where the Asset came from when it was produced from other Assets. Crops, composites, upscales, and exports are the overwhelming majority of images in circulation, and a Record for a composite currently claims a custody chain that begins at import with no statement that two other Assets are inside it.
3.2 Decision
Options considered: (a) an in-Record lineage object; (b) separate lineage and recipe Records; (c) both. The draft selects (c): minimal parentage in the Record, rich recipes out of the Record and bound by hash. (a) alone forces unbounded recipe documents into every Record; (b) alone makes the common case — “this came from those two” — require a second fetch.
ADR-0005 is accepted in model, revised in shape. The revision is the parent reference: an earlier revision of this draft let a parent be identified by a bare record_id, which annex E16 makes producer-scoped and which therefore resolves nowhere, or by a pixel_hash alone, which annex E27 says is a grouping key and not an identity. Both are now excluded from the identifier rule.
3.3 Normative text (DRAFT)
A Record MAY carry
lineage:
parentsis non-empty. Each entry MUST carry at least one **globally resolvable** identifier: eitherrecord_uri, or the pair{producer_id, record_id}. This is a rule about resolvability — being able to obtain a statement about the parent — not about identity strength.- A bare
record_iddoes not satisfy that rule. E16 scopesrecord_idto its producer, so on its own it names nothing outside that producer's namespace. It MAY accompany aproducer_id(which is what makes the pair resolvable) or arecord_uri.- **
content_hashSHOULD be present whenever the Producer holds the parent bytes.** It is the only parent identifier stable across producers (E27), and it is what lets a Consumer confirm that the thing it resolved is the thing the Producer meant. It does not satisfy the resolvability rule on its own, because a digest says what the parent is and not where a statement about it can be found. A parent reference carrying nocontent_hashSHOULD draw a conformance warning, and MUST be an error in a named high-trust profile (§3.5).- **
pixel_hashis an object,{value, cpnp_version}, and never satisfies the resolvability rule.** A parent identified down to apixel_hashresolves to a candidate, not to an identity: a Consumer matching such a reference against its own holdings MUST treat every match as a candidate parent, MUST NOT treat a match as proof that it holds the parent, and MUST NOT use the match as a join key.pixel_hashis rendered-identity grouping (E27): distinct Assets that render to one Canonical Buffer share it by design, which is the property that makes it useful and the property that makes it wrong as a primary key. Thecpnp_versionmember is why the object form exists — a digest without its universe is a digest a Consumer will compare across universes (1.0 §13, E10).- Absence of
lineagemeans “not stated”, never “no parents”. A Consumer MUST NOT infer from a missinglineageblock that the Asset has no parents. Stated explicitly because the opposite reading is the natural one and it is wrong: most Records will omitlineagebecause their Producer does not track it, not because the Asset is original.relationis one of:
derived— one parent, transformed (crop, resize, upscale, colour adjustment, denoise, or any re-encode that changes the Canonical Buffer).composed— this Asset contains pixel content from this parent alongside others. Multi-parent lineage usescomposedfor every contributing parent.exported— this Asset is a re-encoding of the parent with **no change to the Canonical Buffer**: parent and child share apixel_hashand differ only incontent_hash. A rendition that resamples, crops, or re-grades isderived, notexported; so is a re-encode that drops render-affecting metadata, because removing a consumed ICC profile or Orientation tag changes the Canonical Buffer (E1).recipe_sha256, when present, is the SHA-256 of the RFC 8785 JCS serialization of the recipe envelope (§3.4). **recipe_uripresent ⇒recipe_sha256REQUIRED** (schema-encoded, §15.3); a Consumer that fetches the URI MUST verify the document against the digest before use.- A Record carrying a
composechain event MUST carrylineagewith at least twocomposedparents (schema-encoded).lineageis an assertion by the Producer, at trust tierunsignedunless the Record is signed. A Consumer MUST NOT treat lineage as provenance evidence in the C2PA sense; it is a pipeline claim, exactly likeprovenance.chain.lineageMUST NOT contain a cycle. A Consumer that detects one — by followingrecord_uri,{producer_id, record_id}orcontent_hashedges — MUST treat the affected Records as non-conformant.
Lineage depth is deliberately not bounded here; a parent's own lineage lives in the parent's Record. Deep graphs are traversed, not inlined.
3.4 The recipe envelope
recipe_sha256 binds a document whose format was previously unstated, which makes “verify it before use” unactionable: two producers cannot agree on what they are hashing. The envelope is minimal on purpose — VER does not specify image compositing — but it is specified.
A recipe document is served with media type
application/jsonand is a JSON object with this top level:
recipe_versionis REQUIRED and is the envelope's version, not the producer's pipeline version."1"is the only value this draft defines.canonicalizationis REQUIRED and MUST be"rfc8785-jcs". It is stated in the document rather than assumed, so that a document whose digest is being checked says which serialization the digest covers.stepsis REQUIRED and is an array. VER does not define step vocabulary;opnames an operation whose meaning belongs to the producing tool. A Consumer MUST NOT infer pixel semantics from a step it does not recognise.- Any other top-level member MUST be an
x-authority key —x-<reverse-dns-authority>— and a Consumer MUST ignore keys it does not recognise. Unprefixed members other than the three above are not permitted: the envelope has to stay recognisable as an envelope.recipe_sha256is SHA-256 over the RFC 8785 JCS serialization of this object. The digest is a commitment, not a disclosure requirement: a proprietary recipe can be committed to and released later, or never.
Publishing this envelope as its own schema artifact (ver-recipe.schema.json) is a ratification deliverable, listed on RELEASE.md.
3.5 Semantic fixtures required before ratification
Lineage is where a schema check is least useful: every interesting failure is a statement about a graph, not about a document. Eight fixtures MUST exist and be exercised before §3 ratifies, and each names the behaviour it pins:
| # | Fixture | What it must show |
|---|---|---|
| 1 | exported | Parent and child share pixel_hash, differ in content_hash; accepted |
| 2 | composed | compose chain event with two composed parents; accepted |
| 3 | duplicate parent | The same parent listed twice; rejected or warned, and the ruling stated |
| 4 | contradictory | relation: "exported" with parent and child pixel_hash values that differ; rejected |
| 5 | self-parent | A parent reference resolving to the Record itself; rejected |
| 6 | cycle | A → B → A across two Records; rejected, with the detection rule exercised on each edge type |
| 7 | missing digest | recipe_uri with no recipe_sha256; rejected by the schema |
| 8 | dangling | A record_uri that does not resolve; accepted — an unresolvable parent is not a malformed Record, and a validator that made network reachability part of conformance would repeat VER1303's mistake |
The named high-trust profile referenced in §3.3 is also a ratification deliverable: a profile identifier, a published rule list, and the promotion of the content_hash-absent warning to an error within it.
