Media Pipeline

Verification & Share

Public verification page, PDQ lookup, and selective disclosure proof display.

Everything described so far — C2PA extraction, ZK proof generation, on-chain commitment, selective disclosure — converges on a single user-facing feature: the verification page. This is where someone checks whether an image or video is authentic, sees what the uploader chose to disclose, and can independently verify the cryptographic proof chain.

How verification works

The verification page supports two modes: file verification (upload or drag-and-drop an image or video) and proof verification (upload a selective disclosure proof bundle). Both run entirely in the browser — the file never leaves the user's device.

Verification flow: User drops file → Compute PDQ hash (browser) → three parallel lookups (PDQ, vPDQ, TMK) → Match determination → three outcomes (Exact match, Similar match, No match) → Fetch metadata + Merkle proof → Verify Merkle proof (browser) → Display results

Step 1 — hash computation

When a file is dropped onto the verification page, the browser computes perceptual hashes without uploading the file. For images, this is a single PDQ hash (256-bit). For videos, the browser extracts keyframes via WebCodecs, computes a PDQ hash per keyframe, and generates a TMK temporal fingerprint.

Step 2 — backend lookup

The hashes are sent to the backend for matching — not the file itself. Three lookup endpoints run in parallel:

EndpointInputUsed for
PDQ lookupSingle PDQ hash (64 hex chars)Image matching — finds exact or perceptually similar images
vPDQ batch lookupUp to 128 keyframe hashesVideo frame matching — matches individual keyframes against stored media
TMK lookupTMK fingerprint (~257 KB)Video temporal matching — finds re-encoded or speed-changed videos

Step 3 — match determination

The backend returns Hamming distances (number of differing bits between two 256-bit PDQ hashes). The verification page classifies the result:

Match tierConditionMeaning
ExactHamming distance = 0Byte-identical to a verified original
SimilarHamming distance ≤ 31Same perceptual content — likely a crop, resize, or re-encode of a verified original
No matchHamming distance > 31 or no match foundNot found in the verified content database

Step 4 — Merkle proof verification

If a match is found and the content has been committed to the transparency log, the backend returns a Merkle inclusion proof. The browser verifies this independently:

  1. Leaf hash. Compute SHA-256(pdq_hash + salt) from the returned PDQ hash and random salt.
  2. Path reconstruction. Walk up the sibling hashes, combining at each level: SHA-256(min(left, right) + max(left, right)). The canonical ordering prevents position-dependent manipulation.
  3. Root check. Compare the reconstructed root against the on-chain root. If they match, the PDQ hash was genuinely included in the tree at the claimed position.

The Merkle proof state machine tracks four states: none (no proof available), pending (proof exists but not yet committed on-chain), valid (cryptographically verified), and invalid (verification failed).

What the viewer sees

The verification result displays different information depending on the match tier and the uploader's disclosure preferences. The uploader's choices — made at upload time — are locked into the ZK proof and cannot be changed by the viewer.

Disclosed metadata

Each metadata category is displayed only to the granularity the uploader chose:

CategoryDisclosure levelsExample at each level
Locationhidden → continent → country → city → neighbourhood → exactHidden / Europe / United Kingdom / London / Westminster / 51.5007° N, 0.1246° W
Timehidden → year → quarter → month → day → exactHidden / 2026 / Q1 2026 / January 2026 / 28 January 2026 / 28 Jan 2026 14:32:07 UTC
Devicehidden → category → manufacturer → fullHidden / Smartphone / Google / Google Pixel 8 Pro
Softwarehidden → type → name → fullHidden / Camera app / ProofMode / ProofMode v49.2
Authorhidden → has_author → type → fullHidden / Has author / Organisation / reuters.com
Provenancehidden → summary → steps → fullHidden / 2 edits / Camera → Lightroom → Photoshop / Full edit chain with timestamps

Fields marked "verified" have been cryptographically proven — the ZK circuit verified that the disclosed value is consistent with the C2PA manifest's signed data. Fields that are hidden are blinded with Poseidon2 commitments — the viewer cannot learn the hidden values, and the prover cannot fabricate disclosed values that weren't in the original manifest.

Uploader identity

Alongside the image metadata, the verification page shows any identity signals the uploader chose to attach:

  • Organisation. "Verified member of reuters.com" — proven via JWT identity proof, reveals only the email domain.
  • Verified Human. "Verified Human" badge — proven via passport NFC scan, reveals no personal details.
  • Nationality. Country flag and name — optional disclosure from passport verification.

Provenance status

Every verified piece of content carries a provenance status derived from the C2PA manifest analysis:

StatusMeaningDetection method
CameraPure camera capture, no AI involvementHardware device origin, no AI claims or generative actions
AI GeneratedEntirely AI-generatedAI generator claim (DALL-E, Midjourney, Firefly) or c2pa.ai_generated assertion
Camera + AI EditCamera origin with AI modificationsIngredient chain traces to real camera, but generative actions found
UnknownC2PA present but origin unclearManifest exists but origin device not determinable
FailedVerification failedSignature invalid, certificate expired, or trust list mismatch

Selective disclosure proof verification

The second verification mode accepts a selective disclosure proof bundle — a JSON file that cryptographically proves specific claims about an image without revealing the image itself.

Proof bundle structure

SelectiveDisclosureBundle
{
  "version": 2,
  "circuitId": "selective_disclosure_v2",
  "publicInputs": {
    "disclosedContentHash": "0x...",      // Image identity
    "disclosedTrustListRoot": "0x...",    // Oracle root at proof time
    "disclosedProofTimestamp": "0x...",   // When the proof was generated

    // Location (if disclosed)
    "disclosedLocationFlags": "0x01",     // Bounding box enabled
    "disclosedLatMin": "0x...",
    "disclosedLatMax": "0x...",
    "disclosedLonMin": "0x...",
    "disclosedLonMax": "0x...",

    // Time (if disclosed)
    "disclosedTimeMin": "0x...",
    "disclosedTimeMax": "0x...",

    // Commitments (always present, blinded)
    "nullifier": "0x...",
    "rootCommitment": "0x..."
  },
  "proof": "0x...",                       // Hex-encoded ZK proof
  "generatedAt": 1706450000000            // Timestamp (ms)
}

The browser verifies this proof using the same Barretenberg WASM backend that generated it. If the proof is valid, the disclosed fields are guaranteed to match the original C2PA manifest data — the prover could not have fabricated them.

Key Insight
A selective disclosure proof is self-contained and independently verifiable. It does not require network access, backend queries, or blockchain lookups. Anyone with the proof file and the verification circuit can confirm its validity — even offline, even years later.

Sharing verified content

Verify link

The simplest way to share verification is a link to the verify page with a pre-populated hash:

Verify link format
https://apertrue.app/verify?hash={sha256_of_file}

When someone opens this link, the verify page automatically runs the hash lookup against the backend — the viewer doesn't need to upload the file. The link is safe to share on social media, messaging apps, or in articles because it contains only a hash, not the image data.

Proof export

For stronger guarantees, users can export a selective disclosure proof as a downloadable JSON file. This provides cryptographic proof of specific claims without depending on Apertrue's backend availability:

  • Download. The proof bundle is saved as apertrue-proof-{commitment}.json. The recipient loads it on the verify page for independent verification.
  • Copy to clipboard. The JSON is copied for pasting into messages, emails, or documents.
  • Share via URL. The proof can be stored in the browser's localStorage and shared via a URL containing a reference ID: ?sd_id={uuid}. This avoids embedding the full proof (which can be several kilobytes) in the URL.
Use case: legal evidence
A journalist's photo is disputed in court. Rather than trusting Apertrue's servers (which could be compromised or unavailable), the journalist exports a selective disclosure proof proving the image was captured by a specific device type in a specific city on a specific date. The court independently verifies the ZK proof — no reliance on any third-party service.

Verification badges

Throughout the Apertrue interface — in the gallery, on the verify page, and in shared links — verified content carries a verification badge. The badge variant communicates the provenance status at a glance:

BadgeProvenanceDisplayed as
Camera verifiedPure camera captureCamera icon, green
AI generatedFully AI-generatedSparkle icon
Camera + AI editCamera origin, AI edits appliedCamera + sparkle icons
Unknown originC2PA present but origin unclearCircle outline
FailedVerification failedWarning icon
BlockedContent blocked by moderationX icon

Badges are rendered in five size variants (extra-small through large) for different contexts — inline text, gallery cards, and full verification pages. Clicking a badge opens the verification page for that content.

Video verification display

Video verification shows additional information beyond what images display:

  • Keyframe match statistics. The number of matched keyframes out of the total extracted, with the overall match percentage. This tells the viewer how much of the video's visual content matches verified originals.
  • Temporal fingerprint scores. TMK Level 1 (cosine similarity) and Level 2 (temporal alignment) scores, showing how closely the video's temporal structure matches the original.
  • Video metadata. Duration, resolution, codec, and container format — displayed alongside the standard C2PA metadata.

What verification does not require

The verification system is designed to minimise trust assumptions:

  • No account needed. Anyone can verify content without registering or signing in. The verify page is publicly accessible.
  • No file upload. The image or video never leaves the browser during verification. Only perceptual hashes are sent to the backend.
  • No trust in Apertrue. Merkle proofs are verified client-side against on-chain roots. Selective disclosure proofs are verified using the public verification circuit. The backend provides data, but the browser independently checks it.
  • No trust in the viewer. The uploader's disclosure choices are cryptographically enforced. A viewer cannot learn hidden fields or change what is disclosed — the ZK proof constrains exactly what is revealed.
Note
The verify page is intentionally the most publicly accessible part of Apertrue. Verified content is only valuable if anyone — journalists, courts, platforms, the public — can independently check it. Every verification step that can run in the browser does run in the browser.

The next section covers content moderation and compliance — how Apertrue screens content before it enters the platform while maintaining user privacy.