extensions/synthesis.md: the origin claim of an object, a group or a track (captured, reconstructed, assistive, synthesized; the class rises along a chain of derivations and never falls), its IPTC digital source type and C2PA statement as functions of the claim, two pinned integer derivations whose recomputation proves an assistive claim, the marking and detection records of the second layer and their agreement with the first, AI participants' tracks, reviews a player presents only on a decode path of the review's tier, and a player's signed label log with the EU icons pinned by digest. The wai-rs module (feature `synthesis`), its vectors generator and the corpus. Co-Authored-By: Claude Opus 5.5 <[email protected]>
47 KiB
WAI Extension: Synthesis Provenance
Status: Draft. Registers the record
wai.media.synthesis: what a piece of media is, for every kind of media WAI carries. An origin claim says of one object, one group or one track that it is captured, reconstructed (a codec, neural or not, rebuilding what was captured), assistive (an edit that does not substantially alter its input, such as an upscale or a denoise) or synthesized (generated). The claim names each input by the link hash of the input's own claim, and its class is the greater of the class its own step gives and its inputs' classes, so the class rises along every chain of derivations and never falls. The claim restates the content's IPTC digital source type, which is a function of the claim, and so is its C2PA statement: the actions and thec2pa.ai-disclosureassertions a manifest carries. An assistive claim carries evidence: where its step is one of the two integer derivations this extension pins, a verifier holding the input's decoded samples recomputes the output and compares it, so the claim that the edit is assistive is checked, not trusted. The signed claim is the first of two marking layers; the second is a watermark whose payload is a function of the claim, and a verifier refuses a detection that disagrees with it. The watermark algorithm itself is a slot this revision leaves to a later one. An AI participant of a room is marked in its grant, and its messages, captions and tracks carry its machine origin. A review is a signed verdict over one exact reconstruction, which a player presents only for a presentation whose decode path holds the review's tier, so one review holds for every viewer of that output. A label policy says when a label must be shown, and a player's signed log shows whether it was. No new transport. The reference implementation is thesynthesismodule ofwai-rs(featuresynthesis); the corpus islive-conformance/synthesis/, and its second implementation islive-conformance/synthesis_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.Texts read for this revision, each pinned to the version named: the Code of Practice on Transparency of AI-Generated Content, final text of 10 June 2026, Section 1 Measures 1.1 to 1.3 and Section 2 Measures 1.1 and 1.2 and its Annex 1; the EU icons the Commission publishes for that Annex, as fetched on 21 October 2026 from the page it last updated on 13 October 2026 (§10.2); Regulation (EU) 2024/1689, Article 50, paragraphs 2, 4 and 5 as that Code reproduces them, and the last sentence of paragraph 2 in a reproduction of the Official Journal text (the Official Journal itself could not be fetched for this revision); the C2PA Technical Specification, version 2.4, §6.4, §18.15.2, §18.15.4.5 and §18.28; and the IPTC Digital Source Type vocabulary, whose most recent concept change is dated 2024-10-23. A section number of one of those texts in this extension is that version's.
1. Scope
In scope: the records and their encodings (§2); the classes, the steps and the origin claim (§3); the two pinned integer derivations and the evidence that makes an assistive claim checkable (§4); the digital source type (§5) and the C2PA statement (§6) a claim gives; the two marking layers, their records and their agreement (§7); AI participants of rooms (§8); the review and when a player presents it (§9); when a label is shown, the EU icons pinned by digest, and a player's log (§10); verification (§11).
Out of scope: a transport; how a generator, a codec or an editor works; the watermark algorithm of the second marking layer and its detector, which a later revision registers in the slot of §7.4; how a manifest is assembled, embedded and signed, which is C2PA's and provenance's; key discovery for the signers of these records, which a verifier takes from jwp-receipts §7 as it does for any other key; and the rendering of a label, beyond the asset it shows and the instants it shows it at.
Relationship to the core spec: wai.media.synthesis is a record (SPEC §5): a
signed record a WAI tool emits and verifies. It names no payload format and is not dispatchable.
Relationship to timed-text and live-events: a caption's
origin (live-events §4.10), and a chat's or a reaction's (§4.4, §4.5 there), is this
extension's class for a text event, with no record of its own: origin 1 is a person's text,
class captured with source digitalCreation; origin 2 a machine's, class synthesized
generated with oversight fully_autonomous; origin 3 a machine's reviewed by a person, class
synthesized with oversight human_validated. Timed-text §10.3's export is then §5's and §6's
for such a step.
Relationship to energy-binding §6.1: a decode, and any codec, reconstructs;
it does not create, and a neural codec does not make its output AI-generated. This extension
keeps that: a codec is a convert step (§3.2), which names no model, asserts no source of its
own and keeps its input's class, and its C2PA statement is c2pa.opened and c2pa.converted
with no digital source type (§6).
Relationship to determinism-tiers: a pinned derivation of §4 is integer
throughout, so its output is a pure function of its input's samples, and a claim over it is
checked by recomputation, at the tier replicate promises. A review (§9) is presented only where
the presentation holds the review's tier, under the rule of determinism-tiers §2 (What a tier
covers): a tier covers what the sink emits.
Duties this extension supports: the two machine-readable marking layers of the Code of Practice (Section 1, Measure 1.1: Sub-measure 1.1.1, signed and time-stamped metadata; Sub-measure 1.1.2, an imperceptible watermark) and its richer provenance information (Measure 1.3: the model, the timestamp, the type of operations); the disclosure of AI systems that interact with people (Article 50(1)); and the placement of a label at first exposure, after interruptions and at regular intervals (Section 2, Sub-measures 1.2.1 to 1.2.3). WAI states what its records carry and what a verifier checks. It makes no statement about any regime, and a deployment confirms what a regime asks of it against that regime's text.
2. Records
Every record of this extension is a JSON object (RFC 8259) under the encodings of
jwp-receipts §2.4.3: byte strings in lowercase hexadecimal; integers unsigned,
at most 2^53 − 1, in plain decimal; no other number; no boolean; a term and a date-time
as defined there. A record is read as jwp-receipts §3.1 step 1 reads a claim: RFC 8259's grammar,
no member named twice in one object, at most 32 levels of nesting. Each record has exactly the
members its section lists, no member missing and none added, at any level, and a kind naming
it; one that does not is record_malformed.
Every record carries signer_pubkey (32 bytes, the Ed25519 key of RFC 8032 that signs it) and
sig (64 bytes):
sig = Ed25519(signer_key, "wai:synthesis\x01" || JCS(record without "sig"))
link_hash = BLAKE3("wai:synthesis-link\x01" || JCS(record))
JCS is RFC 8785, which on these values reduces to what jwp-receipts §2.4.3 states. The signature
is verified strictly (SPEC §7 Signatures): a key that fails step 1 or 2 is
bad_key, and a signature that fails a later step bad_signature. A record is named by its
link hash wherever another names it. A record whose members carry issued_at holds a date-time
there, the instant its signer signed it: the time stamp the Code of Practice's Sub-measure 1.1.1
asks for, self-asserted.
A digest of samples is SHA-256 (FIPS 180-4) over a canonical buffer: for an image, the RGB8
buffer of reconstruction-binding §2 (row-major, three bytes a
pixel, no row padding); for another medium, the canonical buffer its capability's
*-equivalence names (determinism-tiers §2). An object's content
hash is jwp-receipts §2.1's.
3. Classes and the origin claim
3.1 The origin claim
An origin claim, kind "wai-origin", has these members:
| member | type | meaning |
|---|---|---|
kind |
string | "wai-origin" |
subject |
object | what it is about: {"type": "object", "content_hash"}, an object by its content hash; {"type": "group", "receipt"}, a group by its group receipt's link hash (jwp-receipts §3 step 3); or {"type": "track", "track", "from_group"}, a track by its digest (live-rooms §6.2: SHA-256 over the rendering of its namespace, one zero byte and its name) from the group from_group on |
media |
string | audio, video, image or text |
class |
string | captured, reconstructed, assistive or synthesized (§3.2) |
step |
string | capture, convert, assist or generate (§3.2) |
operation |
term | what ran: a capability or derivation string, or a term naming the type of operation (upscale, inpainting, speech-synthesis) |
record |
32 B hex or null |
the identity of a record that derived the subject, where its deriver signs one; null otherwise |
source |
string | the subject's digital source type (§5) |
models |
array | the models the step ran, at most 16, each {"digest", "type"}: a prior's digest (SPEC §3.1) and trained (a model derived from sampled content) or algorithmic (one that is not); no digest twice |
oversight |
string or null |
for generate: fully_autonomous, prompt_guided or human_validated (C2PA 2.4 §18.28.4); otherwise null |
inputs |
array | the objects the step read, at most 16, each {"content_hash", "origin", "class", "source"}: its content hash, the link hash of its own origin claim or null where it has none, and that claim's class and source, restated; no content hash twice |
evidence |
object or null |
for assist: §4.3; otherwise null |
issued_at |
date-time | when the signer signed it |
signer_pubkey, sig |
§2 |
media, class, step, oversight, a model's type and an input's class hold only the
values the table names, and source and an input's source only the codes of §5. Otherwise
record_malformed.
Who signs: the party that performed the step: the capturing device or its operator, the encoder, the editor, the generator's provider. A claim carries its signer's authority and no more; which signers a verifier trusts is its policy.
A verifier is given at most one origin claim for one subject; a second for a subject an earlier
claim names is claim_conflict.
3.2 Steps and the class lattice
The classes are ordered: captured < reconstructed < assistive < synthesized. Each step
gives a class, and constrains the claim:
| step | class it gives | inputs | models | oversight |
evidence |
|---|---|---|---|---|---|
capture |
captured |
none | none | null |
null |
convert |
reconstructed |
at least one | none | null |
null |
assist |
assistive |
exactly one | any | null |
an object |
generate |
synthesized |
any | at least one | not null |
null |
A capture states as its source one of digitalCapture, computationalCapture,
screenCapture or digitalCreation: a recording of the world, or a person's work made without
a generative model. A claim that breaks the table is record_malformed.
- capture records the world, or a person's authorship: a camera, a microphone, a screen recording, a person typing or drawing.
- convert rebuilds or rearranges its inputs without generating or enhancing anything: an encode, a decode, a transcode, a re-package, a decoder's reconstruction (neural or not), and a derivation that copies samples or composes them (crop, rotation, grid or overlay composition, derived-tracks). A codec names no model: its parameter set is pinned in its envelope, and it reconstructs what its input holds.
- assist edits one input without substantially altering it: an upscale, a denoise, a colour correction. It is the step whose claim needs evidence (§4).
- generate produces content by a model: a generated picture, voice or video, an inpainting, a voice clone, a talking head, a frame generated between two others, an avatar a model drives.
The class of a claim is the greatest of its step's class and its inputs' stated classes. A
claim whose class is another is class_mismatch. So a conversion of a synthesized input is
synthesized, and a transcode, a clip or a re-package can never lower what its input was.
3.3 Checks against what a verifier holds
- Inputs. For each input whose
originnames a claim the verifier holds: that claim's subject is the object of the input'scontent_hash, and itsclassandsourceare the input's. Otherwiseinput_mismatch. An input whose claim is not held is taken as stated. - The subject's own envelope. Where the subject is an object and the verifier holds its
envelope (SPEC §2): an envelope whose capability's registry class is
GenerativeDeterministicorBehavioral(SPEC §7), or whose manifest'sintentiscreate, is generated by every sink that presents it, and its claim'sstepisgenerate. A claim of another step over such an envelope isclass_understated.
3.4 Through every derivation
A party that derives an object from others and publishes it signs an origin claim for it whose
inputs name the inputs' claims; where it signs a record of the derivation too, the claim's
record names that record. A relay forwards claims as jwp-receipts §4.1 has it forward any
receipt, byte for byte, and alters none. So each object a sink receives has a claim, and the
chain of claims behind it reaches the capture or generation it came from:
| derivation | the output's claim |
|---|---|
| a relay forwards an object | none: the object is the same, and its claim holds for it |
an object re-packaged, or a WAI2 component exploded (SPEC §7.1) |
the same object for every rule that names objects by content hash; its claim holds |
| an encode, a transcode, a rung of a ladder, a decoder's reconstruction | convert of the input |
| a crop, a rotation, a composition of several inputs | convert of the inputs |
| an upscale, a denoise, a sink-side enhancement | assist of the input, with evidence (§4) |
| a frame generated between two others, a talking head from a picture and a voice, an inpainting | generate over the inputs |
| a clip of a recording, a segment of a track | the claim of each group or object it holds, unchanged |
A track or group claim covers each object of the track or group; an object claim, where one is given, is the object's.
4. Assistive evidence
4.1 wai-synthesis:upscale2
The derivation wai-synthesis:upscale2, with no parameters ({}), maps a w × h RGB8 buffer
in to a 2w × 2h buffer out. For each pixel (x, y) of in and each of its three bytes,
with x₁ = min(x + 1, w − 1), y₁ = min(y + 1, h − 1), a = in(x, y),
b = in(x₁, y), c = in(x, y₁) and d = in(x₁, y₁):
- out(2x, 2y) = a;
- out(2x + 1, 2y) = (a + b + 1) >> 1;
- out(2x, 2y + 1) = (a + c + 1) >> 1;
- out(2x + 1, 2y + 1) = (a + b + c + d + 2) >> 2.
This is the half-sample interpolation wai.video.int_motion predicts with (SPEC §5), in the same
integer arithmetic.
4.2 wai-synthesis:denoise3
The derivation wai-synthesis:denoise3, with no parameters ({}), maps a w × h RGB8
buffer in to a w × h buffer out: for each pixel (x, y) and each of its three bytes,
out(x, y) = (8 + Σ k(i, j) · in(clamp(x + i), clamp(y + j))) >> 4,
the sum over i, j ∈ {−1, 0, 1}, with k = 4 at (0, 0), 2 where exactly one of i, j is 0, and 1 at the four corners; clamp keeps a coordinate within 0 to w − 1 (or h − 1).
4.3 The evidence
An assist claim's evidence is an object of exactly these members:
| member | type | meaning |
|---|---|---|
tier |
string | replicate: the step is a pinned derivation, checkable by recomputation; attest: it is not, and the claim asserts that the edit is assistive |
derivation |
term | the derivation run |
params |
object | its parameters |
width, height |
integer | the input's size, each at least 1 |
input_samples |
32 B hex | the digest of the input's samples (§2) |
output_samples |
32 B hex | the digest of the output's samples |
tier and the sizes outside these values are record_malformed. replicate evidence names
wai-synthesis:upscale2 or wai-synthesis:denoise3, with params {}, and an output of at most
2^26 bytes (4 · width · height · 3 for the upscale, width · height · 3 for the denoise);
otherwise evidence_invalid. A derivation evaluated in float, or by any operation this
revision does not pin, states attest: the edit may well be assistive, but its claim can only
assert it.
A model-driven derivation joins the pinned set when a capability registers it in integer
arithmetic at decode-equivalence, with its parameter set pinned (SPEC §3.1); its evidence then
names it and its pin, and is checked the same way. This revision registers none.
4.4 Checking the evidence
A verifier is given the decoded samples of the objects it holds (§11.1). For an assist claim, in this order:
replicateevidence that §4.3 does not admit:evidence_invalid.- With
replicateevidence and the input's decoded samples held: they arewidth·height· 3 bytes and their digest isinput_samples(input_samples_mismatch); the derivation is run over them, and the digest of its output isoutput_samples(assistive_recompute_mismatch). The claim is then proved. Without the input's samples it is not recomputed;attestevidence is asserted. - With the subject's decoded samples held (an object subject): their digest is
output_samples(samples_mismatch). - Under a policy that requires proof (§11.1), a claim that is not proved:
assistive_not_proved.
A proved claim says that this output is that pinned derivation of that input, and nothing else:
a fact any verifier holding the input's samples checks, at the tier replicate promises.
5. The digital source type
A claim's source is a code of the IPTC Digital Source Type vocabulary, whose terms are
http://cv.iptc.org/newscodes/digitalsourcetype/ followed by the code. It is a function of the
claim:
| step | inputs | source |
|---|---|---|
capture |
none | as stated: digitalCapture, computationalCapture, screenCapture or digitalCreation |
convert |
one | the input's source |
convert |
several | compositeSynthetic when an input's class is synthesized; else compositeCapture when every input's source is digitalCapture or computationalCapture; else composite |
assist |
one | the input's source when its class is synthesized; else algorithmicallyEnhanced |
generate |
any | the generated source: algorithmicMedia when no model is trained; else compositeWithTrainedAlgorithmicMedia when oversight is human_validated or an input's class is not synthesized; else trainedAlgorithmicMedia |
A claim whose source is another is source_mismatch. The codes are the vocabulary's own
definitions: algorithmicallyEnhanced is an algorithmic modification that leaves the main
content unchanged; trainedAlgorithmicMedia media created by a model trained on captured
content; compositeWithTrainedAlgorithmicMedia an edit made with such a model, and, by C2PA 2.4
§18.28.3's pairing, generated content a person reviewed and approved; algorithmicMedia media
created purely by an algorithm with no sampled training data. A reconstructed claim keeps its
input's code: a codec makes no claim about what it reconstructed.
6. The C2PA statement
A claim gives one statement, the JSON object {"actions": A, "assertions": D} written in JCS
(RFC 8785):
A, the actions of the manifest's first actions assertion (C2PA 2.4 §18.15.2):- a claim with no input or with several:
[{"action": "c2pa.created", "digitalSourceType": U}],Uthe IPTC term of the claim'ssource; - a
convertof one input:[{"action": "c2pa.opened"}, {"action": "c2pa.converted"}], with no digital source type, as energy-binding §6.1 has it for a decode: the input is the parent ingredient, and its own manifest states its source; - an
assistof one input:[{"action": "c2pa.opened"}, {"action": "c2pa.edited", "digitalSourceType": U}],Uthe term ofalgorithmicallyEnhanced; - a
generateof one input: the same,Uthe term of the generated source (§5).
- a claim with no input or with several:
D, onec2pa.ai-disclosureassertion (C2PA 2.4 §18.28) for each model whosetypeistrained, in the claim's order, labelledc2pa.ai-disclosure,c2pa.ai-disclosure__1, … (C2PA 2.4 §6.4):{"label": L, "data": {"modelType": "c2pa.types.model", "modelIdentifier": "tag:transaction.science,2026:wai/model/" ‖ the digest in lowercase hexadecimal}}, and, for agenerate,"contentProfile": {"humanOversightLevel": O}indata,Othe claim'soversight. A model of typealgorithmichas none: C2PA 2.4 §18.28.3 attaches the assertion only where a trained model was invoked. The model is named by digest, as timed-text §10.3 names a caption's.
Embedding the statement in a manifest, and signing that, is C2PA's and provenance's. A rendition
of a playlist whose objects include one of class synthesized SHOULD carry the
public.machine-generated characteristic (draft-pantos-hls-rfc8216bis-22 §4.4.6.1), as
timed-text §10.2 has it for captions.
7. Two marking layers
7.1 The layers
The first layer is the origin claim: signed, time-stamped metadata (the Code of Practice's Sub-measure 1.1.1). The second is an imperceptible watermark embedded in the content (Sub-measure 1.1.2), whose payload is a function of the claim (§7.2). The Code asks for both on the outputs of generative systems and treats fingerprinting or logging as a supplement that is never sufficient alone (Sub-measure 1.1.3); a content-bound commitment, such as a reconstruction binding over decoded pixels, is a supplement of that kind here and not the second layer.
Under a policy that requires both layers (§11.1), an object claim of class synthesized with no
marking record naming it (§7.2) is layer_missing.
7.2 The payload and the marking record
A watermark carrying a claim holds an 8-byte payload: the code of the claim's class (0
captured, 1 reconstructed, 2 assistive, 3 synthesized), then the first seven bytes of
the claim's link hash. Read from the content alone, with no metadata, it says the class and points
at the claim. Seven bytes find a claim; they do not prove one, and the claim's signature does
that. The marking record names the scheme that reads it (§7.4); a signpost in the content naming
the detection solution, as the Code's Measure 3.4(c)(ii) describes one, is not part of this
revision.
The party that embeds the mark signs a marking record, kind "wai-marking":
| member | type | meaning |
|---|---|---|
kind |
string | "wai-marking" |
subject |
32 B hex | the content hash of the marked object |
origin |
32 B hex | the link hash of its origin claim |
scheme |
term | the watermark scheme (§7.4) |
scheme_digest |
32 B hex | the SHA-256 of the scheme's definition |
payload |
8 B hex | the payload embedded |
issued_at |
date-time | when it signed it |
signer_pubkey, sig |
§2 |
A verifier holds the claim the marking names (claim_unknown); that claim's subject is the
marking's object, and the marking's payload is the claim's (§7.2) (marking_mismatch).
7.3 Detection and agreement
A detector that reads a watermark signs a detection record, kind "wai-detection": the
members of a marking but origin, with payload the 8 bytes read, or null where it found no
mark. For each detection, in order, with the origin claim whose subject is the detection's
object (if the verifier holds one) and the first marking whose subject it is (if it holds one):
- With a marking: the detection's
schemeandscheme_digestare the marking's (detection_mismatch). - With no payload read: where a marking says the object was marked,
watermark_absent, the second layer having been removed; otherwise the object is unmarked. - With a payload read and a claim held: the payload is the claim's (§7.2); otherwise
layers_disagree. A content that carries the mark of a synthesized claim while its metadata says captured, or the mark of another claim, is refused here. With no claim held, the detection is not checked.
7.4 The scheme slot
This revision registers no watermark scheme. A scheme is named by a term wai-mark:<name> and
the digest of its definition, which a later revision pins with its embedding as an integer
derivation appended to a generative one, so that a sink that generates content embeds the mark
as part of the reconstruction it is held to, and with its key-free detector. Until then a
detection is a signed statement by its detector and nothing more. The conformance corpus names
wai-mark:conformance, whose definition is the text wai-mark:conformance names no watermark algorithm (synthesis.md section 7.4) and which names no algorithm: it exercises the records and
their agreement, not a watermark.
8. Live rooms
8.1 AI participants and their events
live-rooms §4.4 marks an AI participant: its grant's ai attribute names the
model that acts for it. Its chat, reaction and caption events state origin 2 or 3 with that
model (live-events §4.4, §4.5, §4.10); a room verifier refuses one that does
not, ai_unmarked (live-rooms §9.2 step 6), and a relay does not forward it.
8.2 Its tracks
Every track an AI participant publishes is synthesized media. Its publisher MUST sign, for
each layer track it publishes, a track claim (§3.1, subject of type track) of class
synthesized, step generate, whose models include the model the grant's ai names; the
claim is signed by the participant's key or a room key. A relay MUST NOT forward the objects
of such a track while it holds no such claim.
A verifier given a room (§11.1) replays its lifecycle events in the merged order (live-rooms
§9.2): each join and grant_exchange sets its subject's grant, each leave clears it, and for
each publish and takeover whose subject's grant names a model in ai, each layer track
k from 0 to layers − 1, owned by the subject (its key in lowercase hexadecimal) or by the slot
(_ and its name), of the published media kind, is named by its digest (live-rooms §6.2). A track
with no claim as above is ai_track_unlabelled, at that lifecycle event.
9. Certified synthesis
9.1 The review
A person or an organisation that reviews a synthesized output signs a review, kind
"wai-review":
| member | type | meaning |
|---|---|---|
kind |
string | "wai-review" |
subject |
32 B hex | the content hash of the object reviewed |
origin |
32 B hex | the link hash of its origin claim, of class synthesized |
tier |
string | the tier at which the reconstruction reviewed is portable: decode-equivalence or generation-equivalence (determinism-tiers §2; SPEC §7 GenerativeDeterministic) |
reconstruction |
32 B hex | the digest of the canonical output the reviewer saw (§2) |
decoder |
term | the decoder the reviewer's output came from |
variant |
term or null |
its decode path, where it has more than one |
scope |
string | what was reviewed: accuracy (words, a script), rendering (pixels, a voice, signing), translation |
verdict |
string | approved, approved-with-notes or rejected |
notes |
32 B hex or null |
the digest of the notes, null exactly for approved |
role |
term or null |
a role the reviewer holds for this scope, as the reviewer states it |
issued_at |
date-time | when it signed it |
signer_pubkey, sig |
§2 |
A verifier holds the claim a review names (claim_unknown), whose subject is the review's object
and whose class is synthesized (review_subject_mismatch).
9.2 Presenting a review
A player proves its tier by stating its decode path and the digest of what it presented, in a
present entry of its log (§10.3): the decoder, its variant, the tier the path holds, and the
output's digest. The tier a path holds is the weakest along it: the decoder's tier at its pin
(SPEC §3.1), and none where any float stage, or any processing after the decode that alters the
medium, lies on the path (determinism-tiers §2 What a tier covers: a
tier covers what the sink emits). A verifier that holds the object's decoded samples (the output
of a decode at the stated tier) checks the stated digest against them:
present_output_irreproducible where it differs.
A player MAY present a review, in a review entry of its log, only for an object it has
presented, and only where its latest presentation of the object before that entry:
- presented the review's object, which the verifier holds the review of
(
claim_unknown,review_subject_mismatch); - under a review whose verdict is not
rejected(review_rejected); - on a path of the review's tier (
review_path_not_exact): a path of tiernoneorentropy-consistencynever presents a review made atdecode-equivalence; - with the review's
reconstructionas its output's digest (review_reconstruction_mismatch).
Where these hold, the viewer received the output the reviewer saw, byte for byte: one review holds for every viewer whose presentation is exact at that tier, and for no other.
10. Label display
10.1 When a label is shown
A label policy, kind "wai-label-policy", signed by the party that presents content to
viewers, says which content is labelled and how:
| member | type | meaning |
|---|---|---|
kind |
string | "wai-label-policy" |
classes |
array | the classes labelled, 1 to 4 of §3.2's, in its order, none twice |
visual |
32 B hex | the SHA-256 of the visual label shown with visual media |
audio |
32 B hex or null |
the SHA-256 of the audible label played with audio-only media; null for none |
interval_us |
integer or null |
the longest a labelled presentation goes without a label, in microseconds of the log's clock; at least 1 when not null |
grace_us |
integer | how long after the instant it is due a label may come |
issued_at |
date-time | when it signed it |
signer_pubkey, sig |
§2 |
A label is due, for each kind (visual for video, image and text, audible for audio):
- at first exposure: the first presentation of labelled content of that kind in the session (the Code of Practice's Sub-measure 1.2.1(d));
- after each interruption: the first presentation of labelled content of that kind after the
presentation resumed from an ad break or another interruption (Sub-measure 1.2.2(b); live-ads'
breaks end on a
cueevent, live-events §4.3); - at intervals: where the policy has one, every presentation of labelled content of that kind that is neither, no longer than the interval after the last label (Sub-measures 1.2.2(b) and 1.2.3).
10.2 The EU icons, pinned
The Code of Practice's Annex 1 publishes three EU icons, for content fully generated by AI
(AI GENERATED), for content partially modified by AI (AI MODIFIED), and a basic one (AI),
each in four variations. The Commission publishes them as two archives: SVG
(https://ec.europa.eu/newsroom/dae/redirection/document/129546, SHA-256
f76707b69b546677b173975c018528e5e549e12afb05550f32c2f46500ffbefd as fetched) and PNG
(https://ec.europa.eu/newsroom/dae/redirection/document/129547, SHA-256
aedc51656497a2e8f3e42798e5749a929952f5b262bad368ee3057f58c0f52c7 as fetched). Each file is
pinned here by the SHA-256 of its bytes:
| file | SHA-256 |
|---|---|
LABEL_AI GENERATED_black transparent.svg |
63d28ab55916b4548edff21d5fbcac065a13e5fb936e9b304ff0bada45f2fac1 |
LABEL_AI GENERATED_black.svg |
503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f |
LABEL_AI GENERATED_white transparent.svg |
d19736f6da9d38af3cffce8ba08f3f7658b573e1794c173fcd8b55a5c200ed44 |
LABEL_AI GENERATED_white.svg |
10125cdef3fc60a351df72fe1266bb00ea046086921bd1bb20135f0f07b5ed36 |
LABEL_AI MODIFIED_black transparent.svg |
9ab09f54c1ccef01799af43794cd93a8af6607ba9ac6b9526fb90a02be8ddf3d |
LABEL_AI MODIFIED_black.svg |
2e7349e5eca4ee78eeef160dfef4545c31850932245373a07c5401e73e6599c0 |
LABEL_AI MODIFIED_white transparent.svg |
7ccbce41f9f821a574007b32806ddb6d635ee8bbc5d753ccd05f66f5a3165845 |
LABEL_AI MODIFIED_white.svg |
0c72a7d569a1447e2982a843e156bd5f9cb2ab6f201943c6c68f988dc2f1bb6e |
LABEL_AI_black transparent.svg |
f8e20f1dde8c7d95940da08960b1063620812acc9b4ea96eb5ad051f80f67c21 |
LABEL_AI_black.svg |
58ffe859a4d74829d397f534a988081bcefb716849c7d3b84fa7a026dd1d257f |
LABEL_AI_white transparent.svg |
52d87cb3f6a191dba24796f745e1eeb80d342883acd1a2e6d856342ad853766c |
LABEL_AI_white.svg |
5b3b94ae67fea55c4f2d013d0b8ed5b876c1533555ed0ed60e688f2f3ccf1a50 |
LABEL_AI GENERATED_black transparent.png |
74c8c4ee720c4a3286c3c1b9f216ac9b918ebf449e8c421dde5a40cd216b27e6 |
LABEL_AI GENERATED_black.png |
8d0af57bc93ba3797042a4b56db757d85f924d9b0200b75b8842da7be5e402a2 |
LABEL_AI GENERATED_white transparent.png |
06c4c7c00738e0e44c35f5927e72842ff7a9dd757e0374335b9cc1ba6bb52199 |
LABEL_AI GENERATED_white.png |
f139d8de4b5173f47a777431e7e03d9258d8ea7ee1ee1a062febc7e9fff067fc |
LABEL_AI MODIFIED_black transparent.png |
f48f6c5137834fff87c701eb9a44934ac51c6b5d826b9b5cfead1b88024f8387 |
LABEL_AI MODIFIED_black.png |
0f32e4afbe1459baaf425c78373ea541421ecdb61803438892e2fc07df67da8f |
LABEL_AI MODIFIED_white transparent.png |
1f3b435b0bf775a168309c4ede49b23c7fa94b759a72623932e6ccb7bdef6203 |
LABEL_AI MODIFIED_white.png |
64241cf4f87eb0837bd234cb63716c7d78f4e9dbbb8866f40464ca4e6bfbfead |
LABEL_AI_black transparent.png |
24da93847c04a5c86c714c6cd018c45331b47df32936b617b3c8476933d97fa9 |
LABEL_AI_black.png |
0814d5f04533c118f81aed0d353ea75d1df4171c40b962dba1dfa044a3d84779 |
LABEL_AI_white transparent.png |
5b8c743b679a62453a4bc25070b82bb4db33ad9c68f4ef1b6ef7bb562dd3a4e3 |
LABEL_AI_white.png |
b9041e416fed455722a3aaa06a5ea0eb376d8a8e11385fdc8e3e8ee6998cdf26 |
A policy whose visual is one of these digests shows that EU icon; any other digest names an
equivalent label, which the Code allows (Section 2, Commitment 1). A player that shows a pinned
icon shows exactly those bytes. No audible EU label is published: the Annex says an audio-only
icon will be developed, and the Code's audio work begins with a task force. A policy's audio is
then the digest of the disclaimer or earcon its signer chooses (Sub-measure 1.2.3), until a
revision of this table pins a published one.
10.3 The player's log
A player signs a log of a viewing session, one record per entry, kind "wai-player", under a
key of that session (no identity of the viewer or the device):
| member | type | meaning |
|---|---|---|
kind |
string | "wai-player" |
session |
32 B hex | fresh random bytes of the session, the same in every entry |
seq |
integer | the entry's position, from 0 |
prev |
32 B hex or null |
the link hash of the entry before it; null for entry 0 |
at |
integer | the session clock at the entry, in microseconds; never below the entry before it's |
entry |
object | one of the four below |
signer_pubkey, sig |
§2, the same key in every entry |
The entries:
{"type": "present", "subject", "origin", "class", "media", "tier", "decoder", "variant", "output"}: the player presented an object fromaton: its content hash; the link hash of the origin claim it applied, ornull; that claim's class as it read it, ornull; themediaof §3.1; the tier its path holds (decode-equivalence,generation-equivalence,entropy-consistencyornone, §9.2); its decoder (a term) and variant (a term ornull), as areconstruct-sessionclaim names them (jwp-receipts §2.4.12); and its output's digest, ornullwhere it states none.{"type": "shown", "label", "asset", "trigger"}: the player showed a label:visualoraudio; the SHA-256 of the asset shown; and why, as the player states it:first-exposure,resumeorinterval.{"type": "resume", "after"}: presentation resumed after an interruption,ad-breakorother.{"type": "review", "subject", "review"}: the player presented the review whose link hash isreviewfor the objectsubject(§9.2).
A log whose seq is not each entry's position, whose prev is not the link hash of the entry
before, whose at goes back, or whose session or key changes, is player_chain_broken, at the
first entry that breaks it.
10.4 The obligations
Each present entry whose origin names a claim the verifier holds states that claim's class,
for that claim's object (present_mismatch). Under a label policy, in this order:
-
Every
shownentry's asset is the policy'svisualfor a visual label and itsaudiofor an audible one (label_asset_mismatch). -
Each
presententry whoseclassis one the policy labels is a labelled presentation at itsat, p, of the label kind itsmediaasks for (§10.1). Taking them in the log's order, each kind on its own:- the first of its kind is a first exposure: a
shownentry of that kind is in the log withatat most p +grace_us; - a later one, when a
resumeentry lies between the labelled presentation of its kind before it and it, is a resume: ashownentry of that kind hasatfrom the latest suchresume'satto p +grace_us; - any other, under a policy with an interval, is an interval: a
shownentry of that kind hasatfrom p −interval_usto p +grace_us.
One with no such
shownentry islabel_not_shown, at itspresententry. A label not shown at first exposure is caught by the player's own signed log: the log states the presentation and when the label came. - the first of its kind is a first exposure: a
11. Verification
11.1 Inputs
A verifier is given, each in a list: origin claims, marking records, detection records and reviews; a label policy or none; a player's log or none; the envelopes of the objects it holds (each named by its content hash); the decoded samples it holds, each with the content hash of the object decoded; a room or none, as live-rooms §9.1 gives a room verifier its inputs; and a policy of two members:
| member | values | default | meaning |
|---|---|---|---|
dual_layer |
when-given, required |
when-given |
required: §7.1's rule applies (layer_missing) |
assistive |
accept-asserted, require-proof |
accept-asserted |
require-proof: §4.4 step 4 applies (assistive_not_proved) |
11.2 Steps
The verifier runs these steps and stops at the first refusal, which it reports with the reason and where: a record by its list and index, the label policy, or the room as live-rooms §9.1 states a place in it.
- Reading. Each record, list by list (origin claims, markings, detections, reviews, the label
policy, the log's entries), each in order, reads (§2, §3.1, §3.2, §4.3, §7.2, §7.3, §9.1, §10.1,
§10.3) and its signature verifies:
record_malformed,bad_key,bad_signature. - Origin claims, in order, each by these checks in order: a subject named before
(
claim_conflict); its class (§3.2,class_mismatch); its source (§5,source_mismatch); its inputs and its envelope (§3.3:input_mismatch,class_understated); its evidence (§4.4:evidence_invalid,input_samples_mismatch,assistive_recompute_mismatch,samples_mismatch,assistive_not_proved). - Marking. The markings in order (§7.2:
claim_unknown,marking_mismatch); the detections in order (§7.3:detection_mismatch,watermark_absent,layers_disagree); then, underdual_layerrequired, the origin claims in order (§7.1,layer_missing). - Reviews, in order (§9.1:
claim_unknown,review_subject_mismatch). - The room. Live-rooms §9: a refusal there is this verification's, at the room. A gap is not
a refusal. Then its AI participants' tracks (§8.2,
ai_track_unlabelled). - The log. Its chain (§10.3,
player_chain_broken); then each entry in order: apresententry (§10.4,present_mismatch; §9.2,present_output_irreproducible) and areviewentry (§9.2:claim_unknown,review_subject_mismatch,review_rejected,review_path_not_exact,review_reconstruction_mismatch); then, under a label policy, §10.4's steps 1 and 2 (label_asset_mismatch,label_not_shown).
11.3 What an accepted case states
- for each origin claim, in order: its link hash, class and source, its C2PA statement (§6) as
written, and for an assist claim whether it is
proved,not-recomputedorasserted(§4.4); - for each detection, in order: its object and its result,
agree,unmarkedornot-checked(§7.3); - for each AI participant's track, in the order §8.2 reaches them: its digest and the claim that labels it;
- for each
reviewentry of the log: its index and the review's link hash; - for each labelled presentation that had a label due: its index, the trigger (
first-exposure,resumeorinterval) and the index of the firstshownentry that met it; - the file name of the EU icon the label policy's
visualis, where it is one (§10.2); - the room's own outcome,
acceptorgap, where a room was given.
12. Reasons
The codes this extension adds, each with where it is raised:
| reason | raised at |
|---|---|
record_malformed |
§2, §3.1, §3.2, §4.3, §11.2 step 1 |
bad_key |
§2 |
bad_signature |
§2 |
claim_conflict |
§3.1 |
class_mismatch |
§3.2 |
source_mismatch |
§5 |
input_mismatch |
§3.3 |
class_understated |
§3.3 |
evidence_invalid |
§4.3, §4.4 step 1 |
input_samples_mismatch |
§4.4 step 2 |
assistive_recompute_mismatch |
§4.4 step 2 |
samples_mismatch |
§4.4 step 3 |
assistive_not_proved |
§4.4 step 4 |
claim_unknown |
§7.2, §9.1, §9.2 |
marking_mismatch |
§7.2 |
detection_mismatch |
§7.3 |
watermark_absent |
§7.3 |
layers_disagree |
§7.3 |
layer_missing |
§7.1 |
ai_track_unlabelled |
§8.2 |
player_chain_broken |
§10.3 |
present_mismatch |
§10.4 |
present_output_irreproducible |
§9.2 |
review_subject_mismatch |
§9.1, §9.2 |
review_rejected |
§9.2 |
review_path_not_exact |
§9.2 |
review_reconstruction_mismatch |
§9.2 |
label_asset_mismatch |
§10.4 |
label_not_shown |
§10.4 |
A verifier given a room also raises every code a room verifier raises (live-rooms §11, and the
live-events codes it lists), at the room: ai_unmarked among them, for an AI participant's
event that does not state its machine origin (live-rooms §9.2 step 6).
13. Conformance
live-conformance/synthesis/ holds the corpus, in one file,
vectors.json, written from fixed seeds by wai_synthesis_vectors gen. It lists the EU icons of
§10.2 and the vectors: each a case of §11.1, its outcome, and, accepted, everything §11.3
states; refused, its reason and its place. It has:
- Accepted: a capture, a neural codec's reconstruction of it, and its two pinned derivations,
each proved by recomputation from the reconstruction's decoded samples; a float
super-resolution step whose attest evidence is asserted; a generated video, an algorithmic
audio generation from a
wai.generate.spectralenvelope, an inpainting a person reviewed, a composite of a capture and the generated video, and a transcode of the video, each synthesized object marked in both layers, a detection that agrees and one that finds the capture unmarked; a room whose AI host states its model in its chat and caption and labels its speech track; and a review of the generated video presented on an exact path, with labels shown at first exposure, after an ad break and within an interval. - Refused: each reason of §12 at least once, each a control of one defect: among them a
synthesized object labelled captured (
class_understated), an assistive claim whose recomputation differs (assistive_recompute_mismatch), the two marking layers disagreeing (layers_disagree), a review presented on a non-exact path (review_path_not_exact), an AI participant's unmarked chat (ai_unmarked, raised by the room) and a track it publishes with no claim (ai_track_unlabelled), and a label not shown at first exposure, after a break, and within the interval (label_not_shown).
A conforming verifier gives every vector the outcome, the reason, the place and the statements
the corpus states. wai_synthesis_vectors verify and the test tests/synthesis.rs check that the
file regenerates byte for byte, that wai-rs gives every vector its outcome, that §12 lists
exactly the reference's codes, and that §10.2's table pins exactly the reference's icons.
live-conformance/synthesis_verify.py is the second implementation, written from this text: it
shares no code with wai-rs; it reads §10.2's pins and §12's codes from this text,
verifies each room with the live-rooms corpus's second implementation (live_rooms_verify.py),
every signature with WAI's strict rule (claim-conformance/_ed25519.py) and every link hash with
BLAKE3 written from its specification (claim-conformance/_blake3.py); and it mutates copies of
the corpus and requires every copy to be refused.
14. Security and privacy considerations
- A claim carries its signer's authority. A class is a statement; what this extension adds is that a class can only rise along a chain of claims (§3.2), that an assistive claim over a pinned derivation is checked by recomputation (§4), that a generator's own envelope cannot be claimed as anything but generated (§3.3), and that the second layer is checked against the first (§7). A synthesized object whose producer signs no claim at all is caught only by the second layer, and only once a watermark scheme is registered (§7.4).
- A watermark payload's seven bytes of link hash locate a claim; they are not a commitment, and a mark can be copied into other content. A detection that agrees with a claim says the content carries that claim's mark, not that the content is the claim's subject; the claim's subject is bound by content hash.
- Evidence states digests of samples, never samples. A verifier that recomputes holds the input's decoded samples, which it decoded itself.
- A player's log is signed under a key of the session and names objects, claims, labels and instants of the session's clock: no viewer, no device and no account. It shows what that key reports, and is not evidence that a person watched.
- A review names its reviewer's key and, where given, a role the reviewer states; a role is the reviewer's statement until an authority attests it.
- A relay can withhold a claim but cannot alter one. A sink that receives an object without the claim its chain should hold sees the gap in the chain of inputs (§3.4).