Previous in series Part 1: The Trust Crisis - Why C2PA Exists

Part 1 laid out the problem, why provenance matters, and why C2PA exists. This is where we get into the implementation layer. Before getting into cryptography, you need to understand what is being described, how it is structured, and how that structure maps to the content’s lifecycle.

So being honest, Part 2 is where most people get lost. Stay with me, and by the end, you should have a solid understanding of the data structure behind C2PA and how it is actually used.

What is a manifest?

Let’s start by talking about what a C2PA manifest is and the components that make it up. But before that, take one step back and start at the file. When you sign a piece of content, a C2PA manifest store is embedded into the file, typically using a JUMBF container.

This is known as the manifest store. The responsibility of this store is to hold one or more C2PA manifests. This is what allows a file to build a cryptographically signed history over time as it moves through different stages. Each change to the content results in a new manifest being added to the store. If the file is altered without adding a new manifest, verification of the most recent manifest will fail.

Moving on to the C2PA manifest itself, it is important to separate the physical storage model from the logical model.

What does that mean?

Assertions and ingredients are stored as independent data blocks within the manifest container. That is the storage model. But they are not trusted independently. They are referenced by the claim, and the claim is what gets signed. That is the logical model.

So while assertions and ingredients exist as separate data blocks from a storage standpoint, they are bound together through the claim from a logical standpoint. The signature does not cover them individually. It covers the claim that references them.

This is where a lot of people get tripped up. When you look at the JSON, it can feel like everything lives at the same level. Under the hood, the claim is what ties it all together. When we talk about the manifest and its components in this article, we will be referencing the logical model, not the storage model.

So the manifest is composed of several components, each of which serves a unique purpose. First, you have the claim. The claim is the C2PA data structure that gets signed. If you think in terms of X.509, this is similar to the TBS portion of the certificate. Second, you have the signature. This is the cryptographic signature over the claim, stored as part of the manifest. In X.509 terms, this is the signature that covers the TBS portion. And lastly, you have the manifest metadata. This is the supporting data used to store, reference, and operate on the manifest within the file.

We will break each of these down in more detail below, but this is the overall structure and responsibility of a C2PA manifest. This is where the logical model starts to make sense, and everything else builds on top of it.

What is a claim?

The claim is the top-level signed statement.

It is the wrapper that binds everything together and says the assertions in the manifest describe this specific piece of content. The claim is what gets signed, not the assertions individually. That distinction matters, because once a claim is signed, you cannot pull an assertion out and trust it on its own. The trust applies to the whole document.

A claim contains a few key pieces of information. It carries a cryptographic hash of the content, which binds the claim to the exact bytes of the file. Any change to the content breaks that binding. It also references all assertions and ingredients in the manifest, records the generator that produced it, and includes a timestamp that captures when it was created. You can think of the claim as the title page of the manifest.

In practice, a manifest extracted from a signed file looks something like this:

{
  "claim_generator": "Adobe_Photoshop/26.11.0 c2pa-rs/0.43.0",
  "claim_generator_info": [
    {
      "name": "Adobe Photoshop",
      "version": "26.11.0"
    }
  ],
  "title": "sunset-over-galveston-bay.jpg",
  "format": "image/jpeg",
  "instance_id": "xmp:iid:9b4cb01a-1d2e-4f33-a8e1-7e2c3d4f5a6b",
  "thumbnail": {
    "format": "image/jpeg",
    "identifier": "self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg"
  },
  "ingredients": [
    {
      "title": "sunset.dng",
      "format": "image/x-adobe-dng",
      "relationship": "parentOf",
      "label": "c2pa.ingredient"
    }
  ],
  "assertions": [
    { "label": "stds.schema-org.CreativeWork", "data": { "...": "..." }, "kind": "Json" },
    { "label": "c2pa.actions.v2", "data": { "...": "..." } },
    { "label": "c2pa.hash.data", "data": { "...": "..." } }
  ],
  "signature_info": {
    "alg": "Ps256",
    "issuer": "Adobe Inc.",
    "common_name": "Adobe C2PA",
    "time": "2026-04-15T14:43:02Z"
  },
  "label": "urn:uuid:3e12431f-16dc-478c-9aa1-f9eb40910a0f"
}

The claim_generator_info records the tools involved in producing the content prior to signing. The instance_id is a unique identifier for this specific version of the content. The thumbnail is referenced by a URI into the manifest’s JUMBF container. The assertions array carries the actual facts as {label, data} objects, each of which we will look at next. The signature_info records who signed the claim and when, while the cryptographic signature itself is stored elsewhere in the JUMBF container.

What is an assertion?

An assertion is a single, scoped fact about the content. The spec defines a standard library of assertion types, and any tool or vendor can add their own.

Some examples of standard assertions you will see in the wild:

c2pa.actions assertion: describes what has been done to the content. Captured, edited, color graded, cropped, composed from multiple sources. Each action has its own structure recording what changed and when.

c2pa.creative-work assertion: holds Schema.org metadata. Authorship, copyright, headline, description. The kind of information you might find in a journalism workflow or a stock library.

c2pa.thumbnail assertion: embeds a small preview of the content, useful for verifiers that want to display something even when they cannot render the original.

c2pa.hash.data assertion: is the cryptographic binding from the manifest to the actual bytes of the content. This is what makes the manifest tamper-evident at the byte level, which we will get into in Part 3.

Vendor-specific assertions live alongside these, prefixed with the vendor’s namespace. Adobe has its own. Camera makers have their own. The spec is deliberately extensible. The trade-off is that a verifier might encounter assertions it does not understand. The spec handles that gracefully, but it is a real consideration when you are building consumers of this data.

In practice, the assertion looks like this:

{
  "label": "c2pa.actions.v2",
  "data": {
    "actions": [
      {
        "action": "c2pa.opened",
        "parameters": {
          "description": "Opened a pre-existing file"
        }
      },
      {
        "action": "c2pa.color_adjustments",
        "parameters": {
          "description": "Exposure adjustment"
        }
      },
      {
        "action": "c2pa.cropped",
        "parameters": {
          "description": "Used cropping tools, reducing or expanding visible content area"
        }
      }
    ],
    "metadata": {
      "dateTime": "2026-04-15T14:43:02Z"
    }
  }
}

Three actions, recorded in order. The file was opened, then exposure-adjusted, then cropped. Each action records what was done and any relevant parameters. The metadata.dateTime stamps when this assertion was produced. A verifier reading this knows the edit history at a glance, and because the parent claim is signed and references this assertion by hash, nobody can rewrite that history after the fact without invalidating the signature.

The split into many small assertions is intentional. It lets each fact be evaluated on its own. A verifier that only cares about authorship can read the c2pa.creative-work assertion and ignore the rest. A platform that needs to know whether a photo was retouched can look at c2pa.actions. The granularity also makes the system extensible without breaking older verifiers.

What is an ingredient?

Ingredients are the most underappreciated piece of the model.

An ingredient is content that contributed to the current content. A photo edited from a raw file has the raw as an ingredient. A composite built from three source photos has three ingredients. A video edit that pulls in stock footage has the stock footage as an ingredient.

What makes ingredients useful is that each one can carry its own manifest. The chain of custody emerges naturally from this. You see a finished photo. The photo’s manifest says it was edited from a raw file. The raw file’s manifest is included as an ingredient, and that manifest says the raw file was captured by a specific camera at a specific time. You can walk the chain all the way back to the moment of creation, with each step cryptographically bound to the one before it.

That is the magic of the model, and it is also where most of the practical value of C2PA lives. The signature on the final file is not the only thing being verified. The whole tree behind it is verifiable. If any link in that chain is broken or missing, the verifier knows exactly where.

In practice, most manifests have one or two ingredients. Edits build on a previous version. Composites pull in source material. Some have none. A camera capturing a photo for the first time has no ingredients, because nothing came before. Some have many. A long collaborative document might accumulate a deep tree.

An ingredient block looks like this:

{
  "title": "sunset.dng",
  "format": "image/x-adobe-dng",
  "document_id": "xmp:iid:8a3bc01a-9876-5432-bbb1-1f2e3d4c5b6a",
  "instance_id": "xmp:iid:8a3bc01a-9876-5432-bbb1-1f2e3d4c5b6a",
  "thumbnail": {
    "format": "image/jpeg",
    "identifier": "self#jumbf=c2pa.assertions/c2pa.thumbnail.ingredient.jpeg"
  },
  "relationship": "parentOf",
  "metadata": {
    "dateTime": "2026-04-15T10:30:14Z"
  },
  "label": "c2pa.ingredient"
}

The relationship field carries the most information here. parentOf means this ingredient is a previous version that the current content was derived from. Other values exist for other shapes of relationship, like componentOf when an ingredient is one of several pieces composed together.

If the ingredient is itself C2PA-signed, the block also carries a reference to the ingredient’s own manifest. Walking back through that reference is how a verifier reconstructs the full history, all the way to capture. If the ingredient has no manifest of its own, the chain ends there and the verifier records that as unknownProvenance rather than failing outright.

The recursion is the point. A manifest is a node in a graph, not a flat record.

How does the manifest get attached?

A signed package of facts is only useful if it stays with the content.

C2PA solves this by embedding the manifest directly into the file. The format is called JUMBF, a generic container that gets injected into the file’s existing structure in a way that does not interfere with normal use of the file. A JPEG with a manifest is still a valid JPEG. A PNG with a manifest is still a valid PNG. Any reader that does not understand C2PA simply skips the JUMBF box, the way it would skip any other unknown metadata.

The supported formats include the ones you would expect. JPEG, PNG, TIFF, and DNG for stills. MP4, MOV, and AVI for video. WAV, MP3, and M4A for audio. PDF for documents. The list keeps growing as the standard adds support for additional containers.

A few formats do not support inline embedding cleanly. For those, the manifest can travel as a sidecar file alongside the content. This is rare, and it is a worse outcome because sidecars are easier to lose, but it exists for the formats where there is no clean place to put a JUMBF box.

There is one important caveat to all of this. Embedded manifests can be stripped. Most platforms re-encode content on upload, and the re-encoding usually loses metadata, including the manifest. We will look at this hard in Part 8 when we talk about the gap between spec and shipped reality. For now, just know that attached does not mean indestructible.

What happens when the file is modified?

The model gets interesting when content is edited.

When a tool modifies a piece of content, it does not change the existing manifest. Manifests are immutable. The tool creates a new claim, with a new content hash matching the modified bytes, and adds the previous manifest as an ingredient. The result is a new manifest sitting on top of the previous one, with a clean reference back to its parent.

Over the life of a piece of content, this stack grows. The full history is called the manifest store. The most recent manifest is called the active manifest. When a verifier looks at a file, it reads the active manifest first, then walks back through the ingredients to reconstruct the chain.

Format conversion follows the same pattern. Convert a JPEG to WebP and the conversion tool creates a new manifest, embeds it in the new file, and references the original JPEG’s manifest as an ingredient. The chain continues.

Re-encoding without re-signing does not extend the chain. It breaks it. If a platform decodes the file, modifies it, and re-encodes it without writing a new C2PA claim, the bytes will not match the hash in the most recent manifest. Verification fails. The content will look fine to a human, but the provenance trail is gone.

That sharp boundary is the source of both C2PA’s power and its biggest practical weakness. We will keep coming back to it.

Wrapping up

In Part 3, we move into the cryptography that makes this system work. Everything we have covered so far defines how the data is structured. The next step is understanding how that structure is secured. We will look at how a claim becomes tamper-evident, how signatures are applied and validated, and how identity is bound to the content through certificate chains. This is the layer that turns a structured record into something that can be independently verified and trusted.

Next in series Part 3: The Cryptography Behind Content Credentials