Engineering guide · public fixture proof · updated 24 July 2026

Test whether C2PA Content Credentials survive your CDN

Compare repository source bytes with the final delivered URL, retain inspectable evidence, and detect whether the declared source manifest remains reachable through delivered ingredient ancestry.

Synthetic public fixtures · released v0.3.0 · local-first · configured checks only

The delivery gap

A signed source file is not evidence of the bytes users receive.

Content Credentials are bound to asset bytes. A resize, re-encode, metadata policy, or generated rendition can change what reaches the public URL. The source may still inspect cleanly while the delivered variant has no embedded manifest or no longer links back to the selected source.

01

Manifest disappears

The final bytes contain no manifest even though the repository source does.

02

Ancestry changes

A delivered manifest exists, but its reachable ingredient ancestry does not contain the selected source label.

03

Validation fails

Local C2PA inspection reports an invalid state or a failed validation status for the inspected bytes.

The practical test target is therefore the exact delivery URL, not a screenshot, source-file assumption, or CMS setting. Repeat it for every format, crop, breakpoint, or cache variant that matters to the product.

A narrow technical boundary

Test the declaration without turning provenance into a truth verdict.

Configured C2PA expectations, observations, and narrow meanings
Configured expectationObserved by art50-ciNarrow PASS meaning
Manifest requiredManifest presence in each inspected byte streamA manifest was observed in those exact bytes
Embedded requiredWhether the observed manifest is embeddedThe configured embedded condition was observed
Fail on invalidC2PA reader validation state and statusesNo invalid state or failed status was reported
Source chain requiredReachable ingredient ancestry from the delivered active manifestThe selected source label was reachable
Result boundary

This is a byte-level regression check. It is not a signer trust determination, authenticity guarantee, truth assessment, ownership finding, or legal-compliance conclusion. A local C2PA state of Valid is not a Trusted signer result.

Article 50 does not require C2PA by name. If a team has chosen C2PA as one delivery control, this test can help verify the configured mechanics; it does not provide complete Article 50(2) marking or detection coverage. For disclosure testing, use the Article 50 CI guide.

Public released-version proof

Inspect the evidence before copying the configuration.

The controlled fixture uses a public test key and synthetic source-type data. The source and delivered images render the same test card. The delivered file adds an update manifest whose reachable ancestry contains the source label. The negative control removes the delivered PNG's C2PA caBX chunk and verifies that the remaining bytes equal the unsigned fixture.

Synthetic C2PA public test card labelled not trusted
Synthetic, test-only fixture. The signing key is public and is not a production identity.

PASS

24 Jul 2026, 14:21 UTC

Source ancestry observed in delivered bytes

Tool
art50-ci 0.3.0
Source
Embedded · Valid
Delivered
Embedded · Valid
Source label
urn:c2pa:f039c0ce-…d02e7237
Delivered ancestry
Contains source label
Source SHA-256
3d21ffe863fa…84242c6a
Delivered SHA-256
f5794782cd47…249f07cf

Exact claim: for these exact bytes, released v0.3.0 observed embedded locally valid manifests and found the controlled source label in reachable delivered ancestry. The manifest-free control produced the two recorded failures. This public fixture simulates destructive delivery; it is not a claim about every CDN.

Two assets, one declaration

Compare the source with the final public URL.

Keep the known source file in the repository and point delivered at the exact URL people receive. Both sides are required for the chain comparison.

# .art50-ci.yml
version: 1

project:
  name: generated-media-delivery

output:
  directory: artifacts/art50
  screenshots: false

provenance:
  - id: delivery-image
    source: ./assets/source.png
    delivered: https://cdn.example.com/media/image.png
    requireManifest: true
    requireEmbedded: true
    requireSourceManifestInDeliveredChain: true
    failOnInvalid: true

Do not add expectedDigitalSourceType to this shared pair unless the exact URI must appear directly in both active manifests. v0.3.0 checks that field independently against every configured asset; it does not inherit a source value from delivered ancestry.

Manual baseline with c2patool

Before automating, download the exact delivered bytes and inspect them with the official Content Authenticity Initiative CLI. This establishes what the URL serves at that moment.

curl --fail --location   --output delivered.png   https://cdn.example.com/media/image.png

c2patool delivered.png

Manual inspection is useful for diagnosis. The CI check adds a declared source comparison, scheduled recurrence, structured failure codes, and retained JSON/HTML evidence.

Make delivery observable

Run on changes and against production every week.

# .github/workflows/c2pa-delivery.yml
name: C2PA delivery regression

on:
  pull_request:
  workflow_dispatch:
  schedule:
    - cron: "17 8 * * 1"

permissions:
  contents: read

jobs:
  provenance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: Rubiss/art50-ci@v0.4.0
        with:
          config: .art50-ci.yml
          output: artifacts/art50
          artifact-name: c2pa-delivery-evidence
          retention-days: "30"
          install-browser: "false"

The tagged composite Action supports GitHub-hosted Linux runners. A completed audit uploads the validated report directory before returning 0 for a pass or 1 for failed assertions. Configuration or execution errors return 2; an early error before report creation may produce no artifact.

Pull requests catch configuration and source changes. The scheduled run checks the production delivery path even when no repository change occurs. Each report remains a time-bound observation, so choose a cadence that matches delivery risk and evidence-retention policy.

Inspect the exact v0.4.0 release

Evidence, not a badge

Read the observations that produced PASS or FAIL.

  • Target, resolved target, MIME type, byte count, observation time, and SHA-256 for each inspected asset
  • Manifest presence, embedded state, active label, manifest labels, and reachable ancestry labels
  • Local validation state, individual validation statuses, and directly observed action digital-source types
  • Whether the source active label was observed in delivered ancestry and the exact structured failure codes
  • Matching aggregate JSON, rendered HTML, and one privacy-minimised provenance summary per asset

Diagnose the failure code before changing the policy.

C2PA delivery failure codes and focused engineering checks
FailureWhat was observedFirst engineering check
MANIFEST_MISSINGThe inspected delivery bytes contained no manifest even though the configuration required one.Download the exact URL, record its hash and content type, then inspect resize, format-conversion, metadata, and cache settings.
SOURCE_MANIFEST_NOT_IN_DELIVERED_CHAINThe configured source active label was not reachable through delivered ingredient ancestry.Confirm the pipeline used the intended source revision and that its update or transformation manifest retains the source as a reachable ingredient.
MANIFEST_INVALIDLocal inspection reported an invalid state or at least one failed validation status.Read the retained status codes, compare the inspected hash with the expected rendition, and reproduce with c2patool before re-signing or redeploying.
PROVENANCE_INSPECTION_FAILEDThe reader could not complete inspection, so manifest expectations were not evaluated.Check format support, response completeness, maximum byte limits, and the privacy-minimised inspection error before treating it as a provenance verdict.

A failure is evidence about the inspected target, not a remediation instruction. Preserve the failing bundle first. Then reproduce against the downloaded bytes, change one delivery setting at a time, and rerun both the manual inspection and CI assertion. If a team deliberately changes source identity or provenance policy, review and update the declared expectation rather than weakening it merely to make the job green.

Evidence still deserves a privacy review.

art50-ci does not copy inspected media or full manifests into the evidence bundle. It does retain public URLs, hashes, timestamps, labels, statuses, project names, and optional commit SHAs. Those values can correlate assets or reveal internal naming. Review reports before publishing them and keep customer evidence access-controlled.

Offline verification boundary

v0.3.0 verifies after reading the asset locally. Signer-chain trust, timestamp trust, OCSP fetching, and remote-manifest fetching are disabled. The runner inspects and reports; it does not sign, repair, or rewrite content.

Production checklist

Test every delivery variant that can change the bytes.

  • Use the final public URL, not the CMS preview or origin URL
  • Cover important resize, crop, quality, format, and device variants separately
  • Verify both warm and cold cache paths when the platform can produce different renditions
  • Rerun after media-library, image-optimization, or CDN-policy changes
  • Schedule production checks so delivery drift is detected without waiting for a code change
  • Retain the report with the release and investigate the exact failure code before changing policy

Preservation is not universal. Cloudflare documents an explicit Content Credentials preservation setting; Cloudinary documents delivery-time provenance features; and image libraries such as sharp expose metadata-retention choices. Treat those settings as implementation inputs, then verify the resulting bytes.

Choose the path

Copy the proof for free or choose a monitored launch for delivery and accountability.

Self-serve

Free source-to-delivery check

  • Public test fixtures and exact pass/fail configs
  • Open-source v0.4.0 GitHub Action
  • Published v0.4.0 npm CLI package
  • Your team selects assets, policy, and run cadence
  • Your team reviews and retains the evidence
Reproduce the public proof

Founding offer · 30-day monitored launch

€500 one time

The open-source software remains free to self-serve. €500 buys delivery and accountability—not access to the code.

  • Working release gate for one product and up to five declared checks
  • Configuration and one GitHub Actions workflow integrated
  • Reproducible baseline run and evidence delivered: a confirmed pass or product failure
  • Up to three distinct failed-run causes triaged during the 30-day window
  • Agreed configuration, workflow, evidence index, and runbook handoff

Each browser disclosure assertion, optional first-interaction control, or C2PA provenance item counts as one check.

Check monitored launch fit Read the exact delivery scope

Public 60-second fit check. No product URLs, media, repository access, private details, or payment. Preliminary fit or no-fit comes first; private intake requires explicit opt-in. Daily or weekly monitoring is asynchronous, with no on-call or response-time SLA. Engineering delivery only: no legal advice or certification, C2PA creation or embedding, trust determination, or product fixes. Evidence is handed off; hosted custody is not included.

Result boundaries

Frequently asked questions

Does a Valid result mean the signer is trusted?

No. In v0.3.0, art50-ci verifies locally after reading the bytes, while certificate-chain trust, timestamp trust, OCSP fetching, and remote-manifest fetching are disabled. Valid must not be presented as a Trusted signer result.

Does C2PA prove that content is authentic or true?

No. Content Credentials can bind signed assertions to asset bytes and record provenance. They do not prove that an assertion is truthful, that a person owns the content, or that a product complies with law.

Does every CDN remove Content Credentials?

No. Delivery behavior depends on the service, transformation, format, and configuration. Test the exact public URL and each important rendition instead of assuming preservation or removal.

Why configure both source and delivered assets?

The chain assertion compares the source active-manifest label with the ingredient ancestry reachable from the delivered active manifest. Without both sides, there is no meaningful source-to-delivery comparison.

Should expectedDigitalSourceType go in the shared pair?

Only if that exact URI must appear directly in both active manifests. v0.3.0 evaluates it independently for every configured source and delivered asset; it does not inherit a value from ancestry. Use a separate source-only item when that is the intended policy.

Is C2PA required by Article 50 of the EU AI Act?

No. Article 50 does not require C2PA by name. It may be one technical implementation selected by a team, but this check is not complete marking, detection, or legal-compliance coverage.

Primary technical sources

Start with the specifications and service documentation.

These sources describe C2PA binding and ingredient structures, validation concepts, manual tooling, and current delivery settings. They—not this guide—are authoritative for their respective systems.

Make delivery observable

Start with one source asset and one real URL.

Reproduce the public proof and keep self-serving for free. If you want us accountable for delivery and the first 30 days, the founding monitored launch is €500.