Skip to main content

WAI Extension: Synthesis Provenance

Mirrored from the canonical text at commit b1681667 ().

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 the c2pa.ai-disclosure assertions a manifest carries. An assistive claim carries evidence: where its step is one of the two integer derivations this extension pins, or a super-resolution derivation of exact-upscale under a prior pinned by digest, a verifier holding the input’s decoded samples (and that prior) recomputes the output and compares it, so the claim that the edit is assistive is checked, not trusted. A picture a sink presents and does not publish is named in a claim by the digest of its samples. 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 registered watermark, wai-mark:tent64, is a mark in the luma of pictures and video embedded by a pinned integer derivation, so a verifier that holds the unmarked frames recomputes the marked ones and knows the mark carries its payload and nothing else; anyone reads it with no key, through a code whose false-positive bound is derived and checked; and an attack battery measures what it survives, which is all it claims. A signpost in the content and in the metadata names the detection solution, and a well-known path resolves it. 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 the synthesis module of wai-rs (feature synthesis); the corpus is live-conformance/synthesis/, and its second implementation is live-conformance/synthesis_verify.py. The scheme’s battery and measurement are the synthesis_mark module (feature synthesis_mark); its corpus is live-conformance/synthesis-mark/, and its second implementation live-conformance/synthesis_mark_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, 2.1, 2.3 and 3.2 to 3.4 and its Glossary, and Section 2 Measures 1.1 and 1.2 and its Annex 1, as the Commission’s document 129555, whose SHA-256 as fetched on 21 October 2026 was 7bd22c5a3c56eaefda27a5bf7a6118198ef2a9c9255241bd97abf7cdedf9bc28; 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, §9.3, §18.10, §18.15.2, §18.15.4.5, §18.15.4.8, §18.15.5 and §18.28; RFC 8615; 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, the registered watermark scheme, what it survives, its signpost and its resolver (§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; a watermark for audio or for text, which this revision does not register (audio was in this revision’s sight and is not reached; text needs a scheme of its own); finding a mark again after a crop or a rotation, which the registered scheme does not do (§7.5.8); 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); a detection mechanism given as a public specification with two implementations (Sub-measure 2.1.1), its signed results (Sub-measure 2.1.2), the reliability and robustness measurements of Measures 3.2 and 3.3, and a signpost for the interoperability of detection (Measure 3.4(c)(ii)); 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:

membertypemeaning
kindstring"wai-origin"
subjectobjectwhat 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); {"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; or {"type": "samples", "samples"}, a picture a party presents or holds and does not publish as an object, by the digest of its samples (§2): a sink’s upscale or generated frame (exact-upscale §6.1)
mediastringaudio, video, image or text
classstringcaptured, reconstructed, assistive or synthesized (§3.2)
stepstringcapture, convert, assist or generate (§3.2)
operationtermwhat ran: a capability or derivation string, or a term naming the type of operation (upscale, inpainting, speech-synthesis)
record32 B hex or nullthe identity of a record that derived the subject, where its deriver signs one; null otherwise
sourcestringthe subject’s digital source type (§5)
modelsarraythe 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
oversightstring or nullfor generate: fully_autonomous, prompt_guided or human_validated (C2PA 2.4 §18.28.4); otherwise null
inputsarraythe 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
evidenceobject or nullfor assist: §4.3; otherwise null
issued_atdate-timewhen 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:

stepclass it givesinputsmodelsoversightevidence
capturecapturednonenonenullnull
convertreconstructedat least onenonenullnull
assistassistiveexactly oneanynullan object
generatesynthesizedanyat least onenot nullnull

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.

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

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:

derivationthe output’s claim
a relay forwards an objectnone: 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 reconstructionconvert of the input
a crop, a rotation, a composition of several inputsconvert of the inputs
an upscale, a denoise, a sink-side enhancementassist of the input, with evidence (§4); a picture the sink presents and does not publish is the subject by its samples
a frame generated between two others, a talking head from a picture and a voice, an inpaintinggenerate over the inputs
a clip of a recording, a segment of a trackthe 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₁):

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:

membertypemeaning
tierstringreplicate: the step is a pinned derivation, checkable by recomputation; attest: it is not, and the claim asserts that the edit is assistive
derivationtermthe derivation run
paramsobjectits parameters
width, heightintegerthe input’s size, each at least 1
input_samples32 B hexthe digest of the input’s samples (§2)
output_samples32 B hexthe 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 {}, or wai-upscale:sr2 or wai-upscale:sr4 (exact-upscale §3), with params {"prior"}, the SHA-256 of the prior as 32 bytes of hex (exact-upscale §3.5); and an output of at most 2^26 bytes (4 · width · height · 3 for the upscale and for wai-upscale:sr2, 16 · width · height · 3 for wai-upscale:sr4, 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 text 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. exact-upscale registers two, whose evidence names the prior by digest.

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:

  1. replicate evidence that §4.3 does not admit: evidence_invalid.
  2. With replicate evidence and the input’s decoded samples held: they are width · height · 3 bytes and their digest is input_samples (input_samples_mismatch); the derivation is run over them, and the digest of its output is output_samples (assistive_recompute_mismatch). The claim is then proved. An exact-upscale derivation runs under the prior the verifier holds by params.prior; one that does not read, or an input past the derivation’s limits under it (exact-upscale §3.1, §3.3), has no output (evidence_invalid). Without the input’s samples, or without the prior, it is not recomputed; attest evidence is asserted.
  3. With the subject’s decoded samples held (an object subject), or with a samples subject: their digest, or the subject’s, is output_samples (samples_mismatch).
  4. 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:

stepinputssource
capturenoneas stated: digitalCapture, computationalCapture, screenCapture or digitalCreation
convertonethe input’s source
convertseveralcompositeSynthetic when an input’s class is synthesized; else compositeCapture when every input’s source is digitalCapture or computationalCapture; else composite
assistonethe input’s source when its class is synthesized; else algorithmicallyEnhanced
generateanythe 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):

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 payload is the whole of what a mark carries: it names no viewer, no session, no device and no account (§7.5.6).

The party that embeds the mark signs a marking record, kind "wai-marking":

membertypemeaning
kindstring"wai-marking"
subject32 B hexthe content hash of the marked object
origin32 B hexthe link hash of its origin claim
schemetermthe watermark scheme (§7.4)
scheme_digest32 B hexthe SHA-256 of the scheme’s definition
payload8 B hexthe payload embedded
embeddingobject or nullthe frames the mark was embedded into (§7.6)
issued_atdate-timewhen it signed it
signer_pubkey, sig§2

An embedding is an object of exactly these members: frames, width and height, integers each at least 1, the count and size of the frames; unmarked and marked, each 32 B hex, the SHA-256 of the unmarked and of the marked frames, each frame its canonical buffer (§2), frame after frame. Otherwise record_malformed.

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). Then §7.6.

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 and embedding, with payload the 8 bytes read, or null where it found no mark, and with read, the frames it read: an object of exactly the members frames, width and height (integers, each at least 1) and samples (32 B hex, the SHA-256 of those frames’ canonical buffers, frame after frame), or null; otherwise record_malformed. It is the signed detection result of the Code’s Sub-measure 2.1.2: the content by its hash, the detection solution by the scheme’s term and digest, and a time stamp; and its kind says the result rests on a watermark (Measure 2.3), the claim being the metadata. 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):

  1. With a marking: the detection’s scheme and scheme_digest are the marking’s (detection_mismatch).
  2. Its scheme and the frames it read (§7.4, §7.6): scheme_mismatch, mark_evidence_invalid, samples_mismatch, detection_irreproducible, layer_not_proved.
  3. 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.
  4. With a payload read and a claim held: the payload is the claim’s (§7.2), and the detection agrees (agree); or it is the payload of a claim the verifier holds that the claim reaches through inputs along steps convert and assist, each input’s claim held, and the detection agrees by inheritance (agree-inherited): a conversion that does not mark its output again carries its input’s mark, and its claim names that input. 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 (not-checked).

7.4 Schemes

A scheme is named by a term wai-mark:<name> and the SHA-256 of its definition. This revision registers wai-mark:tent64 (§7.5). Its definition is the JCS text (RFC 8785) of this object, whose bytes are the corpus’s scheme.json (§13):

{"amplitude":12,"cells_per_bit":16,"class_max":3,"code":[255,71,29],"field":"11d",
 "generator":"140a722a1a468d36d87a25364e685922a1e56fd1a478c1d","grid":64,"length":248,
 "luma":[77,150,29,128,8],"margin":[3,2],"max_frame_bytes":268435456,"media":["image","video"],
 "min_height":128,"min_width":128,"orientations":8,"pattern":"tent","sample":"RGB8",
 "scheme":"wai-mark:tent64","specification":"extensions/synthesis.md section 7.5",
 "stream":"wai-mark:tent64 stream"}

(broken here into lines after commas between members; the definition itself has no line break, and no space but those inside its strings), and its digest is 4e1bd9f2cac9420fc276434751a3fb9b2b0916128f200bf147009a86168be7d6. The definition pins the scheme’s parameters, and §7.5 is its algorithm. A marking or a detection that names wai-mark:tent64 under another digest is scheme_mismatch. A marking of the scheme states its embedding, and a detection its read, of frames the scheme admits (§7.5.1): otherwise mark_evidence_invalid.

wai-mark:conformance stays as a scheme with no algorithm: its definition is the text wai-mark:conformance names no watermark algorithm (synthesis.md section 7.4), and a record of it states no frames (mark_evidence_invalid). It exercises the records and their agreement apart from any algorithm, and stands for a scheme a verifier does not implement: a record of it, or of any term this revision does not register, is a signed statement by its signer and nothing more, checked against the claim (§7.2, §7.3) but neither recomputed nor run again (§7.6).

7.5 The scheme wai-mark:tent64

An imperceptible mark in the luma of RGB8 frames, embedded by a pinned integer derivation and read back with no key. Integer throughout: the marked frames are a pure function of the unmarked frames and the payload, the same bytes on every machine, and what the detector reads is a pure function of the frames it reads.

7.5.1 Frames

The scheme marks frames: RGB8 buffers (§2), each W × H pixels with W ≥ 128, H ≥ 128 and W·H·3 ≤ 2^28, and F ≥ 1 of them of one size, with F·W·H·3 ≤ 2^53 − 1. A still picture is one frame; a video’s frames are its decoded frames in presentation order. The luma of a pixel is Y = (77·R + 150·G + 29·B + 128) >> 8. Frames of another size or count are not admitted: the detector reads no mark from them, and a record that states them is mark_evidence_invalid.

7.5.2 The grid and the pattern

A frame is cut into 64 × 64 cells by the column edges xᵢ = ⌊i·W / 64⌋ and the row edges yⱼ = ⌊j·H / 64⌋, i and j from 0 to 64. Cell k = 64·j + i holds the pixels with xᵢ ≤ x < xᵢ₊₁ and yⱼ ≤ y < yⱼ₊₁; its sides are Lₓ = xᵢ₊₁ − xᵢ and Lᵧ = yⱼ₊₁ − yⱼ, each at least 2, and its area is ν = Lₓ·Lᵧ. The grid is a fraction of the frame, so it follows the picture through a rescale or a change of aspect.

The tent over a span of L samples, at position u from 0 to L − 1, is f(u, L) = sgn(c)·(L − |2·|c| − L|) with c = 2u + 1 − L and sgn(0) = 0: negative over the span’s first half and positive over its second, odd about its centre, and summing to 0. A pixel’s weight is ω(x, y) = f(x − xᵢ, Lₓ)·f(y − yⱼ, Lᵧ) in its cell; |ω| ≤ ν. A cell’s statistic over a luma plane is sₖ = Σ ω(x, y)·Y(x, y) and its mass qₖ = Σ |ω(x, y)|, each over the cell’s pixels. A constant added to every luma sample leaves a statistic unchanged; a gain scales it.

7.5.3 The layout and the code

The stream is the sequence of 32-bit words, read big-endian, eight from each digest, of SHA-256(wai-mark:tent64 stream ‖ n) for n = 0, 1, 2, …, the label in ASCII and n a 4-byte big-endian counter. Its words are taken in this order:

  1. the shuffle π: π starts as 0, 1, …, 4095; for i from 4095 down to 1, with r the next word, the entries at i and at r mod (i + 1) are swapped;
  2. the signs ε₀ … ε₄₀₉₅: +1 where the next word is even, −1 where it is odd;
  3. the mask μ₀ … μ₂₄₇: the lowest bit of the next word.

Bit j of the code, j from 0 to 247, is carried by the 16 cells π(16·j) … π(16·j + 15); the cells π(3968) … π(4095) carry nothing. π begins 1787, 1650, 1946, 384, and the corpus states more of it (§13).

The code is the binary BCH code of length 255 and designed distance 59 over GF(2^8) defined by x^8 + x^4 + x^3 + x^2 + 1, α a root: its generator g(x) is the least common multiple of the minimal polynomials of α^1 … α^58, of degree 184, so the code has 71 information bits and corrects 29 errors. Its coefficients from x^184 down, in hexadecimal, are 140a722a1a468d36d87a25364e685922a1e56fd1a478c1d. It is shortened to 248 bits: a payload’s 64 bits b₀ … b₆₃, b₀ the most significant bit of its first byte, are m(x) = Σ bᵢ·x^(63 − i), its codeword is c(x) = m(x)·x^184 + (m(x)·x^184 mod g(x)), and codeword bit j is the coefficient of x^(247 − j): bits 0 to 63 are the payload’s, the rest its parity. This code is chosen because a mark’s errors arrive as independent bits after the shuffle has scattered each bit’s cells over the frame, which a binary code corrects bit by bit; because its decoding is algebraic, so every correct decoder gives the same result; and because a decoder that corrects only within its radius has a false-positive probability that is an exact count (§7.5.6), which a code that decodes by likelihood does not give.

Bit j is transmitted as tⱼ = cⱼ ⊕ μⱼ, with the sign σⱼ = +1 for 1 and −1 for 0. The mask keeps a frame with no structure from reading as a codeword: a flat frame reads every bit as 0, which unmasks to μ, and μ and its complement are each more than 29 bits from every codeword. A bit’s statistic over a luma plane is Tⱼ = Σ εₖ·sₖ and its mass Qⱼ = Σ qₖ, each over its 16 cells.

7.5.4 Embedding

Given unmarked frames and a payload, each frame is marked on its own, under the same payload. For bit j and an integer amplitude a from 0 to 12, the frame at amplitude a for bit j has, at each pixel of each cell k that carries j, the delta

δ = sgn(z)·⌊(2·a·|z| + ν) / (2·ν)⌋, with z = σⱼ·εₖ·ω(x, y)

(a·z/ν rounded to the nearest integer, halves away from zero) added to each of R, G and B, each sum clamped to 0 … 255. Bit j’s amplitude aⱼ is the least a at which the luma of the frame at amplitude a meets the margin 2·σⱼ·Tⱼ ≥ 3·Qⱼ, and 12 where no a does. The marked frame is the unmarked frame with every cell that carries a bit j changed at aⱼ; the cells that carry nothing are unchanged. The cells of different bits are disjoint, so each bit is embedded on its own, and a bit’s statistic in the marked frame is its statistic at aⱼ.

The amplitude is informed by the frame: a bit whose statistic already meets the margin is left as it is (aⱼ = 0), and the frame is changed only as far as the margin asks, up to the cap, which binds where the picture’s own texture opposes the bit most. A bit that does not meet the margin at 12 is left to the code.

Video. Every frame carries the whole payload, embedded on its own, and the detector sums the bits’ statistics over the frames it is given (§7.5.5). The payload is repeated in each frame, rather than the codeword spread over a group of frames, so that any one frame, a clip of any length, a clip with frames dropped or re-timed and a still taken from the video each carry the whole payload, with no position in time to recover; and the frames read together give a stronger reading than one.

7.5.5 Detection

The detector is given frames, F of W × H; frames §7.5.1 does not admit carry no mark. It tries the 8 orientations o = 0, 1, …, 7 in order. A frame seen in orientation o is the W′ × H′ luma plane (W′ = H and H′ = W where bit 2 of o is set, otherwise W and H) whose sample at (x, y) is the frame’s luma at (x₂, y₂), where x₁ = W′ − 1 − x where bit 0 of o is set and x otherwise, y₁ = H′ − 1 − y where bit 1 is set and y otherwise, and (x₂, y₂) = (y₁, x₁) where bit 2 is set and (x₁, y₁) otherwise. The eight are the quarter turns and mirrors, and one of them undoes any of them.

In orientation o, each bit’s statistic is summed over the frames seen in o; the bit read, rⱼ, is 1 where that sum is above 0 and 0 otherwise; and the word is rⱼ ⊕ μⱼ. The word is decoded within a distance of 29: to the codeword at most 29 bits from it, which is unique where it exists, or to none. A codeword whose first payload byte is at most 3, a class code, gives the payload, with the orientation and the number of bits corrected, and the detector stops; otherwise it tries the next orientation. Where none gives a payload, the frames carry no mark. Any decoder that finds the codeword within 29 bits whenever there is one, and none otherwise, gives this result: the reference finds the error locator, the shortest linear recurrence the syndromes satisfy, searches the 248 positions for its roots, and checks that what it corrected has no syndrome.

7.5.6 What it guarantees

7.5.7 The battery

What the scheme survives is measured, not asserted. The battery applies each attack below to marked frames, and to the same frames unmarked, and runs the detector (§7.5.5) on the result. The attacks are integer functions of the frames, and the noise comes from a fixed stream, so a run of the battery gives the same bytes on every machine. The attack at index i of a list, from 0, has the seed i. Below, rnd(n / d) is n / d rounded to the nearest integer, halves away from zero, and every result is clamped to 0 … 255.

Attacks on pictures, in the list’s order:

Attacks on clips: none; clip-int-motion-q8 and clip-int-motion-q24, the clip encoded as wai.video.int_motion with motion (blocks of 16, a search radius of 4, half-pel vectors, step q) by the reference encoder and decoded; frame-drop, the frames 0, 2, 4, …; one-frame, the first frame alone; and scale-1/2 and noise-16, as above, on every frame.

7.5.8 What it survives

The measurement sets are generated by the reference from fixed seeds (§13): spectral, 16 pictures of 512 × 512 from wai.generate.spectral’s generator and its committed model; leaves, 16 dead-leaves pictures of 512 × 512, discs of random colour, size and shading painted over each other, then noise of up to 2 a sample; and clips, 4 clips of 8 frames of 256 × 256, a window panning over two pictures of each kind. Each picture is marked under its own payload. The photographs are 24 lossless photographs of 768 × 512 from a test suite of image coding, not in this repository, measured with the reference (§13) and named by the SHA-256 of their files in the corpus’s README; their column is informative.

Each cell gives the pictures that gave their payload, of those marked, and in parentheses the most bits the code corrected among them. No picture gave another payload, and no unmarked picture, under any attack, gave any.

attackspectralleavesphotographs
none16 / 16 (0)16 / 16 (0)24 / 24 (1)
int-motion-q416 / 16 (0)16 / 16 (0)24 / 24 (1)
int-motion-q816 / 16 (0)16 / 16 (0)24 / 24 (1)
int-motion-q1616 / 16 (0)16 / 16 (0)24 / 24 (1)
int-motion-q3216 / 16 (0)16 / 16 (1)24 / 24 (4)
int-motion-q4816 / 16 (1)16 / 16 (4)24 / 24 (2)
staged-base-q816 / 16 (0)16 / 16 (11)24 / 24 (20)
denoise316 / 16 (0)16 / 16 (0)24 / 24 (5)
denoise3-x416 / 16 (0)16 / 16 (7)24 / 24 (24)
scale-3/416 / 16 (0)16 / 16 (0)24 / 24 (2)
scale-1/216 / 16 (0)16 / 16 (1)24 / 24 (6)
scale-1/316 / 16 (28)0 / 164 / 24 (29)
scale-1/416 / 16 (0)16 / 16 (1)23 / 24 (19)
scale-3/216 / 16 (0)16 / 16 (0)24 / 24 (1)
upscale216 / 16 (0)16 / 16 (0)24 / 24 (1)
aspect-3/416 / 16 (0)16 / 16 (0)24 / 24 (2)
half-then-int-motion-q1616 / 16 (0)16 / 16 (4)24 / 24 (7)
crop-1%16 / 16 (0)2 / 16 (29)4 / 24 (26)
crop-2%0 / 160 / 160 / 24
crop-5%0 / 160 / 160 / 24
crop-10%0 / 160 / 160 / 24
mirror16 / 16 (0)16 / 16 (0)24 / 24 (1)
rotate-9016 / 16 (0)16 / 16 (0)24 / 24 (1)
rotate-0.516 / 16 (1)0 / 160 / 24
rotate-10 / 160 / 160 / 24
rotate-20 / 160 / 160 / 24
noise-416 / 16 (0)16 / 16 (0)24 / 24 (1)
noise-1616 / 16 (0)16 / 16 (0)24 / 24 (1)
brightness+3216 / 16 (0)16 / 16 (0)24 / 24 (4)
brightness-3216 / 16 (0)16 / 16 (0)24 / 24 (5)
contrast-1/216 / 16 (0)16 / 16 (0)24 / 24 (1)
contrast-3/216 / 16 (0)16 / 16 (1)24 / 24 (10)
local-blur16 / 16 (0)16 / 16 (3)24 / 24 (21)
attack on clipsclips
none4 / 4 (0)
clip-int-motion-q84 / 4 (0)
clip-int-motion-q244 / 4 (0)
frame-drop4 / 4 (0)
one-frame4 / 4 (2)
scale-1/24 / 4 (0)
noise-164 / 4 (0)

The scheme claims to survive an attack only where every picture of every column, marked, gave its payload and none, unmarked, gave any. For pictures, these attacks, each with the parameter measured: int-motion-q4, int-motion-q8, int-motion-q16, int-motion-q32, int-motion-q48, staged-base-q8, denoise3, denoise3-x4, scale-3/4, scale-1/2, scale-3/2, upscale2, aspect-3/4, half-then-int-motion-q16, mirror, rotate-90, noise-4, noise-16, brightness+32, brightness-32, contrast-1/2, contrast-3/2, local-blur. For clips: clip-int-motion-q8, clip-int-motion-q24, frame-drop, one-frame, scale-1/2, noise-16.

It does not claim to survive a crop of any size, a rotation by a small angle, or area resampling to 1/3 or 1/4, nor any parameter between or beyond those measured. A crop or a small rotation moves the cells against the grid: half a cell turns a bit’s statistic to its opposite, and the detector searches no shift and no angle. Resampling to 1/4 survives on the measurement sets, where a 512 × 512 cell of 8 × 8 becomes exactly 2 × 2, and lost one photograph; resampling to 1/3 lands cells on fractions of a pixel and is not survived.

Imperceptibility, as PSNR of the RGB8 buffer and SSIM of the luma (8 × 8 windows at a stride of 4, C₁ = (0.01·255)², C₂ = (0.03·255)², the least over a clip’s frames):

setPSNR, least … mean (dB)floor (dB)SSIM, least … mean
spectral44.86 … 45.0236.660.9688 … 0.9732
leaves43.05 … 43.6936.660.9661 … 0.9696
clips41.90 … 42.0838.730.9439 … 0.9495
photographs41.39 … 43.5835.760.9761 … 0.9842

7.6 Proving a marking and reproducing a detection

A verifier is given, beside the records, the decoded frames of the objects it holds and the unmarked frames of marked objects (§11.1). For each marking, after §7.2’s checks, in this order:

  1. its scheme (§7.4): scheme_mismatch, mark_evidence_invalid;
  2. for wai-mark:tent64, with the unmarked frames of its object held: they are frames·width·height·3 bytes and their SHA-256 is the embedding’s unmarked (mark_input_mismatch), and the scheme’s embedding of them (§7.5.4) under the marking’s payload has the SHA-256 marked (mark_recompute_mismatch). The marking is then proved; otherwise it is stated;
  3. with an embedding and the object’s decoded frames held: their SHA-256 is the embedding’s marked (samples_mismatch);
  4. under a policy that requires the second layer proved (§11.1), a marking that is not proved: layer_not_proved.

A proved marking says that the object’s frames are the scheme’s embedding of those unmarked frames under that payload, and nothing else. What it says of the unmarked frames is what the verifier knows of them: for an object a sink generates at an exact tier (§7.8), the verifier generates them itself and the proof covers the whole output; for unmarked frames a provider hands over, it covers the marking step.

For each detection, at §7.3 step 2, in this order: its scheme (§7.4); with a read and the object’s decoded frames held, they are frames·width·height·3 bytes and their SHA-256 is samples (samples_mismatch); and for wai-mark:tent64, the detector (§7.5.5) is run on them, and the payload it reads, or its finding no mark, is the detection’s payload (detection_irreproducible). The detection is then reproduced (reproduced), and otherwise not run again (not-rerun). Under a policy that requires the second layer proved, a detection that is not reproduced is layer_not_proved.

So a detection that reports a mark in content that carries none, or in content altered beyond what the scheme survives (§7.5.8), is refused wherever the verifier holds the content’s frames: a detection agrees with a claim only for a payload the content carries.

7.7 The signpost and the resolver

The Code’s Measure 3.4(c) has its signatories implement an interoperability solution for their detection mechanisms by 2 February 2027; one of the solutions it names (point ii) is a publicly readable signpost in the content that tells the public which detection solution to use without running every provider’s. Its Glossary has a signpost solution be an openly disclosed marking technique within the content, and its detection mechanism, that indicates which watermark detection mechanism to employ where metadata is not available, and says it may be an imperceptible machine-readable mark. This extension gives three:

  1. In the content. wai-mark:tent64 is itself openly disclosed and read with no key (§7.5.5). Run on any content, its detector says whether the content carries a WAI mark, with the false-positive bound of §7.5.6, and the payload it reads gives the class and the claim. The claim’s markings name every scheme marked into its object, each by term and digest, so the mark is the signpost to the object’s other marks as well.
  2. In the metadata. Each marking gives a C2PA statement (C2PA 2.4 §18.15.5, §18.10), {"actions": A, "assertions": D} written in JCS: A is [{"action": "c2pa.watermarked.bound"}], and D holds one soft-binding assertion, {"label": "c2pa.soft-binding", "data": {"alg": S, "alg-params": G, "blocks": [{"scope": {}, "value": P}]}}, S the scheme’s term, G its digest and P the payload, each byte string written here in lowercase hexadecimal and carried as a CBOR byte string in the manifest. The empty scope covers the whole content. The manifest’s assembler adds the action’s hashed reference to the assertion (relatedAssertions, C2PA 2.4 §18.15.4.8). The scheme’s term is not an entry of C2PA’s soft-binding algorithm list, which C2PA 2.4 §9.3.2 recommends alg to name and does not require.
  3. A resolver. A party that serves WAI content MAY answer GET /.well-known/wai-mark/<name> (RFC 8615), for each scheme wai-mark:<name> it uses, with the scheme’s definition, the bytes whose SHA-256 is the scheme’s digest, as application/json; and GET /.well-known/wai-mark/<name>/payload/<p>, <p> a payload in lowercase hexadecimal, with a JSON array of the texts of the origin claims it holds whose payload (§7.2) is <p>, empty where it holds none. The definition names the section that specifies the detector, a public specification any third party implements (the Code’s Sub-measure 2.1.1 (i)), of which wai-rs (synthesis::mark) and live-conformance/_wai_mark.py are two implementations (§13). A client checks the definition’s digest against the term’s before relying on it, and each claim it receives against the payload it asked for and against the claim’s signature. This revision does not register the well-known name.

7.8 A sink that generates

Where an object’s envelope generates (§3.3) and a marking of wai-mark:tent64 names it, the reconstruction a sink is held to is the scheme’s embedding, under the marking’s payload, of the frames its generation produces: the sink generates, then marks, as one integer derivation appended to the generative one, so the exact tier the generation holds (generation-equivalence, determinism-tiers §2) covers the mark, and every viewer at that tier receives the same marked frames. Its log’s present entry states the marked frames’ digest, the embedding’s marked; a review of the object names that digest as its reconstruction (§9.1); and a player that presents the unmarked frames states an output the object does not decode to (present_output_irreproducible, §9.2). A verifier that holds the envelope generates the unmarked frames itself, and so proves the marking (§7.6) without taking anyone’s unmarked frames on trust.

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":

membertypemeaning
kindstring"wai-review"
subject32 B hexthe content hash of the object reviewed
origin32 B hexthe link hash of its origin claim, of class synthesized
tierstringthe tier at which the reconstruction reviewed is portable: decode-equivalence or generation-equivalence (determinism-tiers §2; SPEC §7 GenerativeDeterministic)
reconstruction32 B hexthe digest of the canonical output the reviewer saw (§2)
decodertermthe decoder the reviewer’s output came from
variantterm or nullits decode path, where it has more than one
scopestringwhat was reviewed: accuracy (words, a script), rendering (pixels, a voice, signing), translation
verdictstringapproved, approved-with-notes or rejected
notes32 B hex or nullthe digest of the notes, null exactly for approved
roleterm or nulla role the reviewer holds for this scope, as the reviewer states it
issued_atdate-timewhen 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 pinned integer derivation of §4 run over the decode’s output, under a prior the player holds by digest where it takes one, alters the medium by a pure function of it, and the path keeps its tier; the output’s digest is then the derivation’s. 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:

  1. presented the review’s object, which the verifier holds the review of (claim_unknown, review_subject_mismatch);
  2. under a review whose verdict is not rejected (review_rejected);
  3. on a path of the review’s tier (review_path_not_exact): a path of tier none or entropy-consistency never presents a review made at decode-equivalence;
  4. with the review’s reconstruction as 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:

membertypemeaning
kindstring"wai-label-policy"
classesarraythe classes labelled, 1 to 4 of §3.2’s, in its order, none twice
visual32 B hexthe SHA-256 of the visual label shown with visual media
audio32 B hex or nullthe SHA-256 of the audible label played with audio-only media; null for none
interval_usinteger or nullthe longest a labelled presentation goes without a label, in microseconds of the log’s clock; at least 1 when not null
grace_usintegerhow long after the instant it is due a label may come
issued_atdate-timewhen it signed it
signer_pubkey, sig§2

A label is due, for each kind (visual for video, image and text, audible for audio):

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:

fileSHA-256
LABEL_AI GENERATED_black transparent.svg63d28ab55916b4548edff21d5fbcac065a13e5fb936e9b304ff0bada45f2fac1
LABEL_AI GENERATED_black.svg503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f
LABEL_AI GENERATED_white transparent.svgd19736f6da9d38af3cffce8ba08f3f7658b573e1794c173fcd8b55a5c200ed44
LABEL_AI GENERATED_white.svg10125cdef3fc60a351df72fe1266bb00ea046086921bd1bb20135f0f07b5ed36
LABEL_AI MODIFIED_black transparent.svg9ab09f54c1ccef01799af43794cd93a8af6607ba9ac6b9526fb90a02be8ddf3d
LABEL_AI MODIFIED_black.svg2e7349e5eca4ee78eeef160dfef4545c31850932245373a07c5401e73e6599c0
LABEL_AI MODIFIED_white transparent.svg7ccbce41f9f821a574007b32806ddb6d635ee8bbc5d753ccd05f66f5a3165845
LABEL_AI MODIFIED_white.svg0c72a7d569a1447e2982a843e156bd5f9cb2ab6f201943c6c68f988dc2f1bb6e
LABEL_AI_black transparent.svgf8e20f1dde8c7d95940da08960b1063620812acc9b4ea96eb5ad051f80f67c21
LABEL_AI_black.svg58ffe859a4d74829d397f534a988081bcefb716849c7d3b84fa7a026dd1d257f
LABEL_AI_white transparent.svg52d87cb3f6a191dba24796f745e1eeb80d342883acd1a2e6d856342ad853766c
LABEL_AI_white.svg5b3b94ae67fea55c4f2d013d0b8ed5b876c1533555ed0ed60e688f2f3ccf1a50
LABEL_AI GENERATED_black transparent.png74c8c4ee720c4a3286c3c1b9f216ac9b918ebf449e8c421dde5a40cd216b27e6
LABEL_AI GENERATED_black.png8d0af57bc93ba3797042a4b56db757d85f924d9b0200b75b8842da7be5e402a2
LABEL_AI GENERATED_white transparent.png06c4c7c00738e0e44c35f5927e72842ff7a9dd757e0374335b9cc1ba6bb52199
LABEL_AI GENERATED_white.pngf139d8de4b5173f47a777431e7e03d9258d8ea7ee1ee1a062febc7e9fff067fc
LABEL_AI MODIFIED_black transparent.pngf48f6c5137834fff87c701eb9a44934ac51c6b5d826b9b5cfead1b88024f8387
LABEL_AI MODIFIED_black.png0f32e4afbe1459baaf425c78373ea541421ecdb61803438892e2fc07df67da8f
LABEL_AI MODIFIED_white transparent.png1f3b435b0bf775a168309c4ede49b23c7fa94b759a72623932e6ccb7bdef6203
LABEL_AI MODIFIED_white.png64241cf4f87eb0837bd234cb63716c7d78f4e9dbbb8866f40464ca4e6bfbfead
LABEL_AI_black transparent.png24da93847c04a5c86c714c6cd018c45331b47df32936b617b3c8476933d97fa9
LABEL_AI_black.png0814d5f04533c118f81aed0d353ea75d1df4171c40b962dba1dfa044a3d84779
LABEL_AI_white transparent.png5b8c743b679a62453a4bc25070b82bb4db33ad9c68f4ef1b6ef7bb562dd3a4e3
LABEL_AI_white.pngb9041e416fed455722a3aaa06a5ea0eb376d8a8e11385fdc8e3e8ee6998cdf26

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):

membertypemeaning
kindstring"wai-player"
session32 B hexfresh random bytes of the session, the same in every entry
seqintegerthe entry’s position, from 0
prev32 B hex or nullthe link hash of the entry before it; null for entry 0
atintegerthe session clock at the entry, in microseconds; never below the entry before it’s
entryobjectone of the four below
signer_pubkey, sig§2, the same key in every entry

The entries:

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:

  1. Every shown entry’s asset is the policy’s visual for a visual label and its audio for an audible one (label_asset_mismatch).

  2. Each present entry whose class is one the policy labels is a labelled presentation at its at, p, of the label kind its media asks 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 shown entry of that kind is in the log with at at most p + grace_us;
    • a later one, when a resume entry lies between the labelled presentation of its kind before it and it, is a resume: a shown entry of that kind has at from the latest such resume’s at to p + grace_us;
    • any other, under a policy with an interval, is an interval: a shown entry of that kind has at from p − interval_us to p + grace_us.

    One with no such shown entry is label_not_shown, at its present entry. 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.

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; the priors it holds (exact-upscale §3.1), each named by its SHA-256; the unmarked frames it holds (§7.6), each with the content hash of the marked object they are the unmarked frames of; a room or none, as live-rooms §9.1 gives a room verifier its inputs; and a policy of three members:

membervaluesdefaultmeaning
dual_layerwhen-given, requiredwhen-givenrequired: §7.1’s rule applies (layer_missing)
assistiveaccept-asserted, require-proofaccept-assertedrequire-proof: §4.4 step 4 applies (assistive_not_proved)
second_layeraccept-stated, require-proofaccept-statedrequire-proof: every marking is proved and every detection reproduced (§7.6, layer_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.

  1. 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.
  2. 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).
  3. Marking. The markings in order (§7.2: claim_unknown, marking_mismatch; §7.6: scheme_mismatch, mark_evidence_invalid, mark_input_mismatch, mark_recompute_mismatch, samples_mismatch, layer_not_proved); the detections in order (§7.3 step 1, detection_mismatch; step 2, by §7.6: scheme_mismatch, mark_evidence_invalid, samples_mismatch, detection_irreproducible, layer_not_proved; steps 3 and 4: watermark_absent, layers_disagree); then, under dual_layer required, the origin claims in order (§7.1, layer_missing).
  4. Reviews, in order (§9.1: claim_unknown, review_subject_mismatch).
  5. 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).
  6. The log. Its chain (§10.3, player_chain_broken); then each entry in order: a present entry (§10.4, present_mismatch; §9.2, present_output_irreproducible) and a review entry (§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

12. Reasons

The codes this extension adds, each with where it is raised:

reasonraised 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 steps 1 and 2
input_samples_mismatch§4.4 step 2
assistive_recompute_mismatch§4.4 step 2
samples_mismatch§4.4 step 3, §7.6
assistive_not_proved§4.4 step 4
claim_unknown§7.2, §9.1, §9.2
marking_mismatch§7.2
scheme_mismatch§7.4, §7.6
mark_evidence_invalid§7.4, §7.5.1, §7.6
mark_input_mismatch§7.6
mark_recompute_mismatch§7.6
detection_mismatch§7.3
detection_irreproducible§7.6
watermark_absent§7.3
layers_disagree§7.3
layer_missing§7.1
layer_not_proved§7.6, §11.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:

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 runs the exact-upscale derivations with live-conformance/_int_sr.py, written from that extension’s §3; 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.

live-conformance/synthesis-mark/ holds the registered scheme’s corpus, written from fixed seeds by wai_synthesis_mark_vectors gen: scheme.json, the bytes of the scheme’s definition (§7.4); the frames its vectors name, each a raw RGB8 file frames/<SHA-256>.rgb; vectors.json; and battery.json. vectors.json holds the stream’s first values and the mask; the codewords of six payloads and the decoding of each with 0, 1, 29, 30 and 40 bits flipped; the embeddings of a generated still, a dead-leaves stand-in for a photograph and a clip of three frames, each with its marked frames’ digest and each bit’s amplitude; what the detector reads from stored frames; a battery sample, every picture attack of §7.5.7 applied to the corpus’s still, marked and unmarked, with the digest of each result and what the detector reads from it; and cases of §11.1 whose frames are files. battery.json is the measurement of §7.5.8 over the measurement sets: each picture’s PSNR, floor and SSIM, each attack’s tallies, the unmarked detections, the words of §7.5.6 and the attacks every set survived.

The cases accept a generated still whose marking is proved and whose detection is reproduced, presented with a review at generation-equivalence (§7.8); a clip proved likewise; a re-encoding whose detection agrees by inheritance; a crop beyond what the scheme survives, read as unmarked; and a marking and a detection that are stated. They refuse a marking whose frames carry another claim’s payload and one whose frames have one bit more changed (mark_recompute_mismatch); unmarked frames of another picture (mark_input_mismatch); decoded frames that are not the marked ones (samples_mismatch); a detection of a mark in a photograph that carries none and one in the crop (detection_irreproducible); a capture claim over the marked still (layers_disagree); a marking not proved under a policy that requires it (layer_not_proved); the registered term under another digest (scheme_mismatch); frames below the least size (mark_evidence_invalid); and a player that presents the unmarked frame (present_output_irreproducible). The codes mark_input_mismatch, mark_recompute_mismatch and detection_irreproducible need frames the scheme admits, and only this corpus gives them.

wai_synthesis_mark_vectors verify and the test tests/synthesis_mark.rs check that every file regenerates byte for byte and no other is there, the battery’s among them; that wai-rs gives every vector and case what it states; that §7.5.3’s generator polynomial and §7.5.4’s definition are the reference’s; and that §7.5.8’s claims are attacks the measurement shows survived and §7.5.6’s figures are the measurement’s. wai_synthesis_mark_vectors measure <dir> <out> runs the battery over the PNG files of a directory; §7.5.8’s photographs’ column is its result over the files the corpus’s README names by digest, which this repository does not hold. live-conformance/synthesis_mark_verify.py is the second implementation, written from this text and sharing no code with wai-rs: it reads the scheme’s definition and checks its digest; builds the stream, the shuffle, the signs, the mask and the generator polynomial from §7.5.3; embeds and detects, with a decoder of its own; applies every picture attack of §7.5.7 and requires the battery sample’s results; verifies every case with synthesis_verify.py; checks §7.5.8’s claims against battery.json’s tallies; and mutates copies of the corpus and requires every copy to be refused.

14. Security and privacy considerations