wai: actually write the C2PA manifest the energy binding describes #79
Loading…
Reference in a new issue
No description provided.
Delete branch "wai/c2pa-emitter"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
energy_bindingandreconstruction_bindingdefine assertion shapes. Nothing emitted them — the extension document was describing a manifest that did not exist anywhere. This addsc2pa_emit, which signs a real asset with the C2PA SDK and puts all three assertions in the manifest, behind an off-by-default feature.Four things that only surface when you actually try
Each is now normative in §6 of
extensions/energy-binding.md.A reconstruction is not
c2pa.created. C2PA 2.x requires every created action to carry adigitalSourceType, 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 fortrainedAlgorithmicMediabecause 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 aparentOfingredient, the manifest opens it,c2pa.convertedrecords 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, notgathered— C2PA's own distinction between a claim the signer stands behind and material merely carried along, 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 (whatadd_actiondoes on its own) yields a manifest that signs happily and every validator then rejects asassertion.action.malformed— on the far side of a signature.Signed is not trusted. An ephemeral certificate reaches
Valid, neverTrusted.untrusted_local_signeris named for its limit.The rule the SDK cannot enforce
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::newrefuses that pair at construction, before a signature can cover it, andrecoverapplies the same rule on the way back in.recoveralso 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_jsonfunctions the extension documents pin, so the emitter cannot drift from the specified shape.Verification
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 — not hypothetical: the missing inception action was found exactly that way.cargo tree -i c2paunder--features provenancefinds nothing, so the default build is untouched.🤖 Generated with Claude Code
008b8a06203e9b1297283e9b1297288348a6e5b38348a6e5b3237257b2ad237257b2add5ec1abe1b