Skip to main content

Ingredients

An ingredient is a prior asset bound into the manifest: the parent a derivative was made from, or a component composited in. Chaining ingredients across signing stages forms a provenance chain, where each manifest names its parent and a verifier walks the chain backward.

You never set an ingredient's relationship directly. You state provenance facts — where the asset came from and what was brought into it — and the signer derives each ingredient's C2PA relationship (parentOf, componentOf, inputTo) and the lifecycle action that goes with it.

The Ingredient shape

Everywhere an ingredient appears (origin.parent or a Component.asset) it is the same object:

FieldRequiredMeaning
datayesThe ingredient's bytes (typically already signed). The SDK extracts its C2PA manifest client-side and sends only that, never the full asset.
formatnoExplicit MIME for a container the SDK can't sniff; inferred from data otherwise.
titlenoHuman-readable title override for provenance-less ingredients.
thumbnailnoA preview tile for this ingredient. See Thumbnails.

Where an ingredient goes

The signer decides the relationship from where you place the ingredient in the request:

  • Parentorigin: { parent }. The asset was derived from this one; the signer adds it as the parentOf ingredient and derives c2pa.opened. An edit names its source capture; a re-encode names its original.
  • Placed component → a Component with event: "placed". An asset composited or assembled into this one; the signer derives componentOf + c2pa.placed. Use event: "removed" for something taken out.
  • Input → a Component with event: "inputTo". An asset that fed a process (for example source frames fed to an AI generation step); the signer derives the inputTo relationship and no action.

Parent (a derivative)

const { sidecar } = await sdk.sign(edited, "edited.jpg", {
origin: { parent: { data: captureSigned } },
actions: [{ kind: "edited" }],
}, { storage: { mode: "embedded" } });

Components (composited in)

Each component has a caller-chosen id; workflow actions can reference that id through componentIds to say which components an action acted on.

const { sidecar } = await sdk.sign(composite, "composite.jpg", {
origin: { sourceType: "digitalCreation" },
components: [
{ id: "logo", asset: { data: logoSigned }, event: "placed" },
{ id: "backdrop", asset: { data: backdropSigned }, event: "placed" },
],
actions: [{ kind: "edited", componentIds: ["logo", "backdrop"] }],
}, { storage: { mode: "embedded" } });

Provenance chains across edits

Re-encoding is where the chain earns its keep. Any transformation that rewrites the asset bytes (a transcode, a format conversion, a destructive edit) invalidates the parent's embedded manifest, because the manifest's data hash no longer matches the new bytes. The parent is not lost: it is carried forward as the origin.parent ingredient on the new manifest. The new manifest attests the new bytes; the ingredient preserves the link to the validated parent. See the newsroom sidecar chain for a worked three-stage example.

Streams

Live streams chain manifests across signing key epochs rather than across edits, but the principle is the same: each epoch's init manifest references the prior one through origin.parent. See Key rotation.

See also