Pixels Lose Their History
A rendered image doesn't reveal whether it came from a camera, an editor, or a generator. C2PA attaches a signed provenance graph, but the manifest still has to be opened, summarized, and checked.
manifestlens reads C2PA data with the official Python library and surfaces the active manifest, claim generator, signer context, ingredients, actions, assertions, validation entries, and hard-binding state.
Separate Integrity From Truth
A valid credential supports provenance integrity and signer context. It doesn't prove the depicted event happened, or that every step before signing was honest. The report keeps those apart, and is available from the CLI, the API, the workbench, the Docker image, and an integration test against a real signed sample.
The Demo
The demo manifest is valid and hard-bound, with one ingredient and three actions: opened, cropped, and color adjusted. The report also surfaces the issuer and signing time.
A Validator That Failed Open, and Two Lies About Real Files
The first bug was a fail-open. A missing validation_status and an empty one were treated identically, but an empty list means "checked, nothing wrong" while an absent field means no check ran at all. Anyone posting to the API could mark an unverified manifest valid by leaving one field out. Related: has_hard_binding only checked whether a hash-assertion label existed, so a manifest whose validation explicitly reported a hash mismatch still claimed a valid binding.
Auditing this page, I did the thing the project is actually for: took the committed, genuinely signed C2PA JPEG and flipped 400 bytes deep in its image data. It works. valid drops to false, the hard binding clears, and the error is assertion.dataHash.mismatch. That's now a committed test rather than a claim.
Running it also caught two signals that were wrong on real files and invisible on the synthetic demo. Hard binding was read from assertion labels only, but a real asset records its binding as a success code under validation_results and never as a label, so genuinely bound files reported has_hard_binding: false while my hand-written demo reported true. And signer trust was dropped entirely: only codes ending in mismatch counted as errors, so signingCredential.untrusted appeared nowhere and the report read as clean.
Both are fixed, and the second one changed the shape of the report. Integrity and trust are different questions, so it answers both: valid for integrity, signer_trusted and validation_warnings for the credential. 11 tests pass across Python 3.10 through 3.13.
Standards Basis
The implementation follows the C2PA manifest model and uses the official Content Authenticity Initiative tooling for asset inspection. Read the C2PA manifest model.
Try It
git clone https://github.com/ahmeddoghri/manifestlens && cd manifestlens
pip install -e .
manifestlens demo
manifestlens inspect demo/C_with_CAWG_data.jpg # a really signed asset
python -m unittest tests.test_real_asset_tamper -v # flip bytes, watch it fail
manifestlens serve # workbench on :8090