Announcement
Introducing provemark: C2PA Content Credentials for PHP
PHP finally has a first-class way to build, sign, read and verify C2PA Content Credentials — including the machine-readable marking of AI-generated content that the EU AI Act, Article 50, will require. It is open source, framework-agnostic, and ships with a working signing service so you can prove the whole chain on your own machine today.
The gap
Content Credentials — the C2PA standard behind the "CR" pin you now see on images from Adobe, Leica, and a growing list of cameras and generators — is how a piece of media carries a signed, tamper-evident record of where it came from. The tooling around it is real and maturing fast, but it grew up in JavaScript, Rust and Python. If your application is written in PHP — and a large share of the web's publishing, CMS and media platforms are — there was no maintained, idiomatic way in.
That gap is about to matter to a lot of people. The EU AI Act, Article 50 requires providers of generative-AI systems to mark their output as artificially generated in a machine-readable way. C2PA is the mechanism the industry is converging on to do exactly that. A PHP shop that needs to comply shouldn't have to shell out to a foreign runtime or reverse-engineer a manifest format to get there.
What it does
The library is built around the full lifecycle, verified end-to-end against a real signature
that c2patool accepts:
- Build a C2PA claim-v2 manifest, including the Article 50 AI marking.
- Sign it — the signing key stays isolated behind a service, never on your web server (more on why below).
- Read the manifest back out of a signed asset.
- Verify both the signature and whether the certificate is trusted — two very different questions, as the certificates guide explains.
A framework-agnostic core does the work; a thin Laravel integration (service provider, facade, queued jobs, artisan commands) wires it into an app with a config file and nothing else. The core has no framework dependency at all — the boundary is enforced in CI, not just by convention.
composer require provemark/content-credentials
The Article 50 marking, done right
Marking content as AI-generated in C2PA is deceptively subtle. It is a single actions assertion
whose first action is c2pa.created, carrying a digitalSourceType set to
the full IPTC URI for algorithmically-generated media, plus the name of the tool that made it.
Get the label or the URI slightly wrong and the manifest is either malformed or silently fails to
say what you meant.
provemark treats that assertion as the core use case and gets it right by construction — one correct actions assertion, no duplicates, well-formed under claim v2. That is the difference between "we embedded some metadata" and "a verifier will actually report this as AI-generated."
Two decisions worth knowing about
The signing key is isolated. Signing happens behind a small HTTP service that holds the private key; the PHP application talks to it over a narrow, authenticated contract and never touches key material itself. This is the single most important architectural property for a credential system — it means a compromise of your web tier does not hand an attacker your signing identity. The library ships that service as a working reference implementation.
It is spec-driven. Every feature exists as a written specification with tests before a line of implementation. That is unusual for a young open-source library, and deliberate: provenance tooling is exactly the kind of software where "it seemed to work" is not good enough, and where a reader needs to trust that behaviour is pinned down, not incidental.
Where to start
- Install it:
composer require provemark/content-credentials(Packagist). - Read the code: github.com/provemark/content-credentials — specs, ADRs and all.
- Understand certificates first: the valid ≠ trusted guide covers the part of C2PA where everyone gets stuck.
It is early days, and I would rather have a handful of careful users than a splash. If you work with media provenance, AI-generated-content compliance, or just want C2PA in a PHP stack, I would genuinely like to hear what breaks and what is missing.