Media Authenticity¶
Two questions can be answered about a media file without a model's opinion: does its cryptographic provenance hold, and does its own metadata contradict what somebody said about it. Both are answered here. Both are deterministic — an auditor with the same file reaches the same result.
A third question — was this generated? — is answered by a model, and is handled differently on purpose. See Advisory signals.
The response has two sections, and they never merge¶
POST /scan/media returns deterministic and advisory as separate lists.
| Section | What is in it | Can it raise risk on its own? |
|---|---|---|
deterministic |
Facts with known ground truth — a signature verifies or it does not; a container names an encoder or it does not | Yes |
advisory |
A model's opinion, as a three-value band plus what that model was never trained on | No |
They are different kinds of claim. A single findings list would invite a reader to act on the weaker one as though it were the stronger, so the API gives them no shared shape to be confused in.
risk_score is 0.0 by construction unless a deterministic hard positive
fired. A model that cannot generalise to generators it has not seen must not
be able to block traffic by itself.
No accuracy figure appears anywhere in the response. There is no field that could carry one — asserted structurally, not by convention.
Content Credentials (C2PA)¶
Where a file carries a C2PA manifest, the manifest is verified against the bytes and the result is exactly one of five states.
| State | Meaning | Hard positive |
|---|---|---|
no_manifest |
The file carries no Content Credentials | No |
manifest_unreadable |
A manifest is present and could not be parsed | No — and not clean either |
manifest_valid_but_signer_unknown |
Signature intact, signer not on the deployment's trust list | No |
manifest_verified_and_trusted |
Signature intact, signer trusted | No |
hard_binding_mismatch |
The manifest does not match the file it is attached to | Yes |
Why the last one is the only hard positive¶
hard_binding_mismatch is cryptography, not interpretation: the bytes changed
after signing, or the manifest was lifted from a different file. It is the one
result in the entire media layer that is not a probability.
Why the middle states are kept apart¶
The reference C2PA library reports a file signed by an attacker's own
certificate authority as "Valid". A product that rendered that as verified
would certify anything carrying any signature. manifest_valid_but_signer_unknown
is the state most implementations get wrong, and on a default trust
configuration it is what almost every real file produces.
Why a verified manifest is not a tick¶
Where a manifest verifies, the platform prints the origin the file declares verbatim. The most common verified result in 2026 is a valid, trusted signature stating that a model generated the content. A green check mark on that inverts its meaning.
Why manifest_unreadable is its own state¶
"We could not check this" and "there was nothing to check" are different
answers and only one of them is reassuring. Folding the first into
no_manifest would turn an analyzer failure into a clean result.
Absence is not evidence
Most files in circulation carry no Content Credentials at all. Platforms
strip manifests on upload and any re-encode breaks the binding, so
no_manifest is the ordinary state of an ordinary file.
Provenance claim consistency¶
Somebody says "this is the original, straight off my phone." The container says FFmpeg wrote it. Cameras do not write FFmpeg.
Pass a claim form field to turn this on. The vocabulary is closed:
| Claim | Contradicted by |
|---|---|
camera_original |
A container naming a re-encoder or an editing suite as its writer |
unedited |
The same, restricted to editing suites |
A claim outside that vocabulary returns HTTP 400. Answering "nothing inconsistent found" to a question nobody asked would read as endorsement of it, so an uncheckable claim is refused rather than answered.
The three outcomes¶
| Outcome | Meaning |
|---|---|
contradicted |
The file's own metadata disagrees with the claim |
no_contradiction_found |
We looked and found no disagreement |
not_checkable |
The file carries no writer metadata to compare against |
There is no "claim verified" outcome and there will not be one. Three independent reasons, any one of which is sufficient:
- Metadata can be stripped. Every messaging app and social platform does it on upload, so its absence is the normal state of an ordinary file.
- Metadata can be forged. It is a string in a container.
- A deepfake played on a monitor and filmed with a real camera produces a genuine camera original, with real sensor noise and real capture metadata. It would pass this check, and it should — the check is about the file's history, not the truth of what it depicts.
no_contradiction_found is the strongest available answer and it is
deliberately not a pass.
A contradiction is not an accusation¶
A broken C2PA binding is cryptography. A contradicted claim means a person described a file inaccurately, which is far more often loose language than an attack — trimming a video re-encodes it, and so does sending it through anything. The finding states a fact about the file's history and attributes no intent to anyone, and it is not a hard positive.
Advisory signals¶
A synthetic-speech model is in build. When it ships it will report a three-value band beside a statement of what it was not trained on — never a verdict, never a number, and never able to block on its own.
Video synthesis classification is planned and not built, and it carries a harder constraint than audio does. Published detectors of that kind transfer poorly to generators they were not trained on, so on a real corpus the most likely output is a false positive against genuine footage.
That constraint shapes the design rather than sitting beside it as a caveat. The signal is built to be survivable when it is wrong: it never names, identifies or describes a person, it is advisory only, and it has no path to a blocking decision. A finding says a file carries synthesis-like artifacts. It never says a person is not who they appear to be.
No accuracy figure will accompany any advisory signal at any point.
Why we are training rather than licensing
Every published detector audited fails its licence chain at some layer — most commonly training data restricted to non-commercial research, which no backbone swap can cure. Ours is a classification head over a permissively licensed backbone, trained on data we hold commercial rights to end to end. See ADR-0003.
The control that does not depend on detection¶
Media analysis is not how wire fraud is stopped. See Compliance & Attestation for out-of-band payment verification, which works whether or not the voice on the call was synthetic.
Validation checklist¶
- a signed fixture returns
manifest_verified_and_trusted; the same file with one byte altered returnshard_binding_mismatch - an unsigned fixture returns
no_manifestand not an empty result claim=camera_originalagainst an FFmpeg-written file returnscontradictedclaim=definitely_realreturns HTTP 400- a scan with no
claimfield emits zero claim findings - a PDF posted here returns
analyzed: falsewith a routing reason, not a clean result
Fixtures with expected verdicts live in scripts/detection-eval/media-corpus/.