Visible label + IPTC DigitalSourceType (XMP) + signed C2PA manifest. Read back with independent tools.
NASA public-domain photo + planet composited by code → labelled as compositeWithTrainedAlgorithmicMedia
| Check (tool) | Before | After |
|---|---|---|
| IPTC DigitalSourceType — exiv2 0.27.6 | ✗ not present | ✓ …/digitalsourcetype/compositeWithTrainedAlgorithmicMedia |
| IPTC DigitalSourceType — exiftool 12.76 | ✗ not present | ✓ …/digitalsourcetype/compositeWithTrainedAlgorithmicMedia |
| C2PA — c2pa-python 0.37.12, default trust | ✗ none — ManifestNotFound: no JUMBF data found | ✓ Valid (signingCredential.untrusted) |
| C2PA — c2pa-python, TEST root as trust anchor | ✗ none — ManifestNotFound: no JUMBF data found | ✓ Trusted |
| C2PA — c2patool 0.26.0, default | ✗ Error: No claim found | ◐ Invalid (signingCredential.untrusted) |
| C2PA — c2patool, TEST root as trust anchor | ✗ Error: No claim found | ✓ Trusted |
| c2pa.actions digitalSourceType | — | ✓ …/digitalsourcetype/compositeWithTrainedAlgorithmicMedia |
| Visible label | ✗ none | ✓ “Edited with AI” · 37 px · contrast 17.74:1 |
Fully procedural image → labelled as trainedAlgorithmicMedia (stand-in; the accurate code for this file is algorithmicMedia)
| Check (tool) | Before | After |
|---|---|---|
| IPTC DigitalSourceType — exiv2 0.27.6 | ✗ not present | ✓ …/digitalsourcetype/trainedAlgorithmicMedia |
| IPTC DigitalSourceType — exiftool 12.76 | ✗ not present | ✓ …/digitalsourcetype/trainedAlgorithmicMedia |
| C2PA — c2pa-python 0.37.12, default trust | ✗ none — ManifestNotFound: no JUMBF data found | ✓ Valid (signingCredential.untrusted) |
| C2PA — c2pa-python, TEST root as trust anchor | ✗ none — ManifestNotFound: no JUMBF data found | ✓ Trusted |
| C2PA — c2patool 0.26.0, default | ✗ Error: No claim found | ◐ Invalid (signingCredential.untrusted) |
| C2PA — c2patool, TEST root as trust anchor | ✗ Error: No claim found | ✓ Trusted |
| c2pa.actions digitalSourceType | — | ✓ …/digitalsourcetype/trainedAlgorithmicMedia |
| Visible label | ✗ none | ✓ “Created with AI” · 29 px · contrast 17.74:1 |
Each labelled file was pushed through one step at a time and read back. IPTC = Xmp.iptcExt.DigitalSourceType via exiv2 0.27.6 · C2PA = c2pa-python 0.37.12 validation · Label = label box in frame + tesseract OCR of the label region.
✓ survived◐ partly / degraded✗ lost– not applicable
| Pipeline step | Composite · JPEG 1024×1024 | Synthetic · PNG 1200×800 | ||||
|---|---|---|---|---|---|---|
| IPTC DST | C2PA | Visible label | IPTC DST | C2PA | Visible label | |
| controlUnchanged copy (control) | ✓ present | ✓ Trusted* | ✓ readable (37.0 px) | ✓ present | ✓ Trusted* | ✓ readable (29.0 px) |
| pillow-resavePillow re-save (default options) | ✗ stripped | ✗ removed | ✓ readable (37.0 px) | ✗ stripped | ✗ removed | ✓ readable (29.0 px) |
| pillow-resave-keep-xmpPillow re-save, XMP/EXIF passed through explicitly | ✓ present | ✗ removed | ✓ readable (37.0 px) | ✓ present | ✗ removed | ✓ readable (29.0 px) |
| im-resize-768wImageMagick resize to 768 px wide (WordPress 'medium_large'-like) | ✓ present | ✗ broken | ✓ readable (27.8 px) | ✓ present | ✗ removed | ✓ readable (18.6 px) |
| im-resize-300ImageMagick resize to fit 300x300 (WordPress 'medium'-like) | ✓ present | ✗ broken | ✓ readable (10.8 px) | ✓ present | ✗ removed | ✓ readable (7.2 px) |
| im-thumb-150-cropImageMagick 150x150 centre crop (WordPress 'thumbnail'-like) | ✓ present | ✗ broken | ◐ tiny, OCR fail (5.4 px) | ✓ present | ✗ removed | ◐ 38.9% in frame |
| im-crop-bottom-12pctImageMagick crop: keep top 88% (e.g. re-framing for a social post) | ✓ present | ✗ broken | ✗ cropped out | ✓ present | ✗ removed | ✗ cropped out |
| cwebp-defaultcwebp -q 80 (default: -metadata none) | ✗ stripped | ✗ removed | ✓ readable (37.0 px) | ✗ stripped | ✗ removed | ✓ readable (29.0 px) |
| cwebp-metadata-allcwebp -q 80 -metadata all | ✓ present | ✗ removed | ✓ readable (37.0 px) | ✓ present | ✗ removed | ✓ readable (29.0 px) |
| pillow-webpPillow save as WebP (default options) | ✗ stripped | ✗ removed | ✓ readable (37.0 px) | ✗ stripped | ✗ removed | ✓ readable (29.0 px) |
| jpegoptim-losslessjpegoptim (default, lossless, keeps markers) | ✓ present | ✗ removed | ✓ readable (37.0 px) | – n/a | – n/a | – n/a |
| jpegoptim-strip-alljpegoptim --strip-all | ✗ stripped | ✗ removed | ✓ readable (37.0 px) | – n/a | – n/a | – n/a |
| exiftool-stripexiftool -all= (strip all metadata) | ✗ stripped | ✗ removed | ✓ readable (37.0 px) | ✗ stripped | ✗ removed | ✓ readable (29.0 px) |
| im-stripImageMagick -strip | ✗ stripped | ✗ removed | ✓ readable (37.0 px) | ✗ stripped | ✗ removed | ✓ readable (29.0 px) |
| im-png-to-jpgImageMagick PNG -> JPEG conversion | – n/a | – n/a | – n/a | ✓ present | ✗ removed | ✓ readable (29.0 px) |
| optipng-strip-alloptipng -strip all | – n/a | – n/a | – n/a | ✗ stripped | ✗ removed | ✓ readable (29.0 px) |
| Survived, of 13 common pipelinesexcl. control and re-sign | 7/13 | 0/13 | 11/13 | 7/13 | 0/13 | 11/13 |
| im-resize-768w-resign remedy, not a common pipelineImageMagick 768 px + new C2PA manifest signed (c2pa-aware CMS step) | ✓ present | ✓ Trusted* | ✓ readable (27.8 px) | ✓ present | ✓ Trusted* | ✓ readable (18.6 px) |
* “Trusted” = c2pa-python validation_state with the TEST root CA supplied as trust anchor; with default trust settings the same files read “Valid” with signingCredential.untrusted. “broken” = APP11/JUMBF bytes still present after ImageMagick resize but the manifest fails to parse/validate (“invalid embedded file box”). OCR accepted “Al” for “AI” (sans-serif capital I). “readable” means tesseract read the label after 4× upscaling; human legibility at small pixel sizes was not tested. WordPress, Instagram, Meta, LinkedIn, X, TikTok and CDNs were NOT tested; the ImageMagick/cwebp/Pillow steps only emulate common operations. Tools: ImageMagick 6.9.12-98 · cwebp 1.3.2 · jpegoptim v1.4.7 · OptiPNG 0.7.8 · exiftool 12.76 · Pillow 12.2.0.
Opaque badges: 24/24 variants pass (min 17.74:1). Translucent black box (alpha 90/255) over the sun: 0/3 pass (worst 2.48:1) — avoid see-through labels.
python3 work/build.py # stand-ins, labels, exiftool XMP write, C2PA signing (c2pa-python) python3 work/labels.py # label variants + contrast python3 work/survival.py # readback (exiv2, exiftool, c2pa-python, c2patool) + 15 pipeline steps exiv2 -q -K Xmp.iptcExt.DigitalSourceType -Pv FILE exiftool -s3 -XMP-iptcExt:DigitalSourceType FILE c2patool FILE trust --trust_anchors root.pem
Prepared by Ali Karabüyük · alikarabuyuk.com · 6 October 2026. Not legal advice.