provemark/content-credentials
provemark/content-credentials is a Laravel/PHP package for issuing, signing, and verifying tamper-evident content credentials. It helps attach provenance and authenticity metadata to digital content and validate credentials within your application.
The package is a thin Laravel integration over provemark/c2pa-verifier: you hand it a file, it returns the verifier's JSON report (validation_status, plus ingredient deltas with success/failure), and you decide what that means for your app. The first thing to internalise is that this package does not invent its own trust model — almost everything you'll build keys off that JSON.
Minimal path:
composer require the package, then confirm the resolved verifier version: composer show provemark/c2pa-verifier. It must be 0.7.x. v0.19.2 declares conflict: <0.7 || >=0.8, so a stale lock file will either fail loudly on update or silently leave you on 0.6 if you never update.php artisan vendor:publish) and read it end to end — it's small. Look for the verifier binary path/timeout and any policy knobs for which validation_status entries count as failure.Start with one image, dump the raw report, and read the validation_status array before writing any mapping code. The report shape is the contract; your enum comes second.
Verify-on-ingest, decide later. Run verification when a file is uploaded, persist the raw JSON report alongside the media record, and keep policy decisions out of the upload path. Provenance verdicts change as the verifier improves (this release is proof), so the raw report is the durable artefact and your derived status is a cache you can rebuild.
Normalise the status once. Don't string-match validation_status codes across controllers and Blade templates. Wrap the report in a small value object or enum with a fromReport() constructor, and keep the code-to-meaning table in one class. When 0.7 introduces new informational lines, you change one file.
Separate hard failures from informational ingredient deltas. The release notes are explicit that signer-acknowledged ingredient failures now surface as informational. Iterate the ingredient deltas, bucket on failure vs success, and render informational entries as context ("this manifest references an ingredient that failed validation") rather than as a rejection. A signer saying "yes, I know" is not the same as a broken signature.
Queue video verification. 0.7.0 adds working Pixel Camera video verification, which means BMFF files now actually get processed rather than short-circuiting. Videos are large and the verifier is an external process — dispatch a job, set explicit timeouts, and don't run it in the request that accepted the upload.
Integration tips:
revalidate artisan command that walks stored media and re-runs verification, rewriting derived statuses from fresh reports. You will want this after every verifier bump.MediaVerified) carrying the normalised status and the report ID, so notifications, moderation queues, and audit logs subscribe rather than poll.c2pa.hash.data scope exclusion, one with ingredient failures. Keep those fixtures in the repo; they're your regression suite against verifier behaviour changes.This "patch" is a security fix in disguise. Verifier 0.6.x would call a file Trusted when the signed hash scope excluded the parts that mattered — a c2pa.hash.data covering only the manifest store, or only a PNG's pixel data, or a c2pa.hash.bmff excluding /mdat or /moov — with the picture swapped after signing. That is exactly the attack C2PA exists to prevent. The floor excluding 0.6 is deliberate; do not "solve" a dependency conflict by relaxing it. If something in your tree pins 0.6, fix that instead.
Old verdicts are not trustworthy. Any Trusted result your app stored while running 0.6 may be a false positive. Either backfill with the revalidation command or visibly mark pre-upgrade verdicts as stale. Do not silently carry them forward — that's the one place where this release can bite you in production data rather than in CI.
"Trusted" is a narrow claim. Even on 0.7, a passing result means the signed assertions verified against the bytes in scope. It says nothing about whether the signer is someone you trust, or whether the asset was manipulated before signing. Treat it as one signal in a policy, not an authenticity badge. Certificate-chain and signer-identity checks are your job.
Version drift is silent until it isn't. Log the verifier version at boot or on first use and assert the major/minor at runtime. A host image built months ago can quietly ship an old binary. Pin the verifier in composer.lock, and add a CI step that fails if the resolved version falls outside ^0.7.
Debugging: always log the full report on unexpected statuses, not just the mapped enum. The interesting information is usually in which specific assertion failed and what its scope was. Reproduce outside Laravel by invoking the verifier binary directly on the same file — it isolates "package bug" from "verifier behaviour".
Extension points worth using: the config's status-policy mapping (customise which codes are fatal), the reporter binding (decorate it to add timing/metrics without forking), and any events the package fires around verification — hooking those is cheaper than wrapping every call site.
How can I help you explore Laravel packages today?