Provenance & C2PA
Provenance & C2PA
Layer 1 of the Verbitas stack implements the C2PA 2.4 specification. A C2PA manifest is a JUMBF-encoded, COSE-signed record embedded in an asset or stored alongside it. It answers: who signed this, when, and what claims did they make about its origin.
What goes in a manifest
A manifest contains one or more assertions — typed key-value claims about the asset. Verbitas supports the following assertion types:
| Assertion | Description |
|---|---|
ai_generated | Boolean — content is fully AI-generated |
ai_assisted | Boolean — human creator with AI edits |
generator | Name of the generating system (e.g. stable-diffusion-xl) |
model | Model identifier |
prompt_hash | SHA-256 of the generation prompt. Never the prompt text itself. |
created_at | ISO 8601 creation timestamp |
creator | Creator identity (editorial recipes) |
location_hint | Approximate location (editorial recipes) |
legal_entity | Legal entity name (enterprise recipes) |
legal_case_ref | Case reference string (legal-evidence recipe) |
deepfake_disclosure | Required for image-deepfake-v1 and audio-voiceclone-v1 |
The assertions included in a given sign call are determined by the recipe.
Signing architecture
Asset + assertions │ ▼ apps/worker: builds C2PA claim │ ▼ POST /internal/sign apps/signer (Tailscale-internal only) │ ▼ AWS KMS eu-central-1 │ ▼ COSE_Sign1 signature + X.509 cert chain + RFC 3161 timestamp │ ▼ apps/worker: assembles JUMBF manifest, embeds in assetThe signer (apps/signer) is the only service that calls AWS KMS. No other service has the IAM permission. The signer accepts only 32-byte canonical C2PA claim digests — it is not a generic signing oracle.
Manifest embedding
| Format | Method |
|---|---|
| JPEG | APP11 segment |
| PNG | caBX chunk |
| WebP | XMP metadata |
| Audio / Video | Sidecar .c2pa file |
Sidecar .c2pa file |
The embedded or sidecar manifest URI is also stored in Verbitas object storage at https://m.verbitas.io/manifests/<asset_id>/manifest.c2pa.
Ingredient chain
When an asset is edited or derived from another Verbitas-signed asset, the new manifest can reference the parent manifest as an ingredient. This creates a chain of custody:
Original image (manifest A) └─ Cropped + color-corrected (manifest B, references A as ingredient) └─ Published to wire (manifest C, references B)Recipients verifying manifest C can traverse the entire chain. The recipe field require_ingredient_chain: true enforces that all edits create new manifest nodes.
OCSP and certificate revocation
Every Verbitas signing certificate is checked against its OCSP responder at verification time.
- Production (
VB_ENV=prod): OCSP status must begood. Revoked or unreachable OCSP returns arevokedorocsp_unavailableverification state. - Development (
VB_ENV=dev): OCSP failure downgrades to a warning; it does not fail verification. A loud log line is emitted.
Enterprise recipes with ocsp_mode: required never degrade — the check is mandatory regardless of environment.
RFC 3161 timestamps
Every manifest includes an RFC 3161 timestamp token from a trusted timestamping authority. The timestamp proves the asset existed at a specific point in time, independent of the signing certificate’s validity period. This is particularly important for legal-evidence use cases where certificates may have been rotated since signing.
Anchoring (blockchain timestamps)
Beyond the RFC 3161 timestamp, Verbitas optionally anchors a SHA-256 Merkle root of manifest digests to:
- Tier 1 (OpenTimestamps / Bitcoin): hourly batch, ~60 min to Bitcoin block inclusion
- Tier 2 (Arbitrum One): hourly batch, near-instant L2 finality, periodic L1 anchor
The anchor proves that the manifest existed before a specific blockchain block. The payload on-chain is exactly 32 bytes — the Merkle root. No PII, no manifest content, no asset bytes.
See concepts: soft-binding for how anchor matches affect the verification state.
Parser security limits
The C2PA parser enforces hard limits to prevent denial-of-service attacks:
- 32 MiB maximum manifest size
- CBOR nesting depth ≤ 32
- Depth-bomb protection
- Magic-byte file type validation (file extensions are never trusted)
What the manifest does not prove
A valid manifest proves:
- The signing certificate was valid at sign time
- The claim digest matches the manifest content (tamper-evident)
- The assertions in the manifest were submitted by the API caller with that key
A valid manifest does not prove:
- The assertions are accurate (e.g.
ai_generated: falsecould be a lie by the submitter) - The asset has not been edited in ways that preserve the manifest (sidecar manifests are detachable)
- Legal compliance with any specific regulation
See Compliance: Positioning for approved language.