open-standards/wai/extensions
Repository files (latest commit first)
Filename Latest commit message Latest commit date
David Charlot 299688bdec wai: actually write the C2PA manifest the energy binding describes
`energy_binding` and `reconstruction_binding` define assertion *shapes*. Nothing
emitted them. A shape no implementation writes is a proposal, and the extension
document was describing a manifest that did not exist anywhere — so this adds
`c2pa_emit`, which signs a real asset with the C2PA SDK and puts all three
assertions in the manifest, behind an off-by-default `c2pa_emit` feature.

Writing it turned up four things that only surface when you try, each of which
is now normative in §6 of the extension:

**A reconstruction is not `c2pa.created`.** C2PA 2.x requires every created
action to carry a `digitalSourceType`, and that vocabulary describes how the
*content* came to exist — captured, human-drawn, model-generated. A decoder does
not know that; it reconstructed pixels from a codestream and whatever was encoded
could have been any of them. Reaching for `trainedAlgorithmicMedia` because a
codec has a neural stage would assert that a compressed photograph is
AI-generated content, which is false, and nothing in the vocabulary means
"decoded; origin not visible from here". A decode is a **conversion**: the
codestream is a `parentOf` ingredient, the manifest opens it, `c2pa.converted`
records what was done, and no action claims a source type at all.

**The codestream has to travel with it.** The whole construction turns on a
verifier re-decoding. A manifest carrying the reconstruction digest but not what
produces it hands over a digest and no way to reach it. The digest says which
decode the joules describe; the parent ingredient says what to decode to get
there.

**The assertions are `created`, not `gathered`** — C2PA's own distinction between
a claim the signer stands behind and material merely carried along, which is the
same distinction the acquisition ladder draws inside the figure. Load-bearing,
not editorial: a validator looks for the inception action among created
assertions, so leaving the actions assertion gathered (what `add_action` does on
its own) yields a manifest that signs happily and every validator then rejects as
`assertion.action.malformed` — on the far side of a signature.

**Signed is not trusted.** An ephemeral certificate reaches `Valid`, never
`Trusted`. `untrusted_local_signer` is named for its limit.

The emitter enforces one rule the SDK cannot. Third-party assertion bodies are
opaque to C2PA, so it will sign a manifest whose energy figure is measured over
one decode while the reconstruction assertion commits to another — the
self-refutation §2 describes — and the result validates.
`ReconstructionManifest::new` refuses that pair at construction, before a
signature can cover it, and `recover` applies the same rule on the way back in.
`recover` also reports the unaccompanied standard assertion — perfectly valid
C2PA, refused here — as its own error rather than a generic absence, because that
is the exact situation the companion exists to be distinguished from.

Assertion bodies are parsed from the same `to_json` functions the extension
documents pin, so the emitter cannot drift from the specified shape.

Verified: 7 tests, signing a real PNG and reading the manifest back with an
independent `Reader`. The trust test pins the single failure code as well as the
verdict, so a real defect cannot hide behind the expected one — which is not
hypothetical: the missing inception action was found exactly that way.
`cargo tree -i c2pa` under `--features provenance` finds nothing, so the default
build is untouched.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 06:24:56 -04:00
..
avatars.md wai: interactive + temporal cargo classes, provenance, telepresence 2026-06-13 23:16:42 -04:00
confidentiality.md wai: register confidentiality (MLS+SFrame) + document opt-in JWP freshness (#37) 2026-07-04 21:16:39 -04:00
determinism-tiers.md wai: ingest the determinism-tier spectrum for neural decode 2026-08-01 19:44:45 -04:00
energy-binding.md wai: actually write the C2PA manifest the energy binding describes 2026-09-06 06:24:56 -04:00
energy-measurement.md wai: publish the energy-measurement methodology (extensions/energy-measurement.md) 2026-08-26 08:14:53 -04:00
haptics.md wai: interactive + temporal cargo classes, provenance, telepresence 2026-06-13 23:16:42 -04:00
interactive-worlds.md wai: interactive + temporal cargo classes, provenance, telepresence 2026-06-13 23:16:42 -04:00
jwp-receipts.md wai: publish the energy-measurement methodology (extensions/energy-measurement.md) 2026-08-26 08:14:53 -04:00
linear-film.md wai: film reel — wai.film.reel, cuts over one world 2026-06-17 02:38:59 -04:00
provenance.md wai: interactive + temporal cargo classes, provenance, telepresence 2026-06-13 23:16:42 -04:00
quantum-ops.md wai-quantum-ops: spec (extensions/quantum-ops.md) + SPEC.md verify-half pointer 2026-08-01 08:40:14 -04:00
quantum-sim.md wai-quantum: Add the energy-accounted receipt (QuantumReceipt + wai_quantum_meter) 2026-07-30 23:10:18 -04:00
reconstruction-binding.md wai: ingest the determinism-tier spectrum for neural decode 2026-08-01 19:44:45 -04:00
score.md wai: music as instructions — wai.audio.score (Phase 6) 2026-06-14 04:29:10 -04:00
spatial-audio.md wai: interactive + temporal cargo classes, provenance, telepresence 2026-06-13 23:16:42 -04:00
state-feeds.md wai: interactive + temporal cargo classes, provenance, telepresence 2026-06-13 23:16:42 -04:00
ternary-tt.md wai-ternary: Productionize wai.ternary.tt — WQTT container, registry entry, receipt, spec 2026-07-30 23:10:18 -04:00
volumetric.md wai: interactive + temporal cargo classes, provenance, telepresence 2026-06-13 23:16:42 -04:00