From ab23c8ab6b9ada101874643323817bc599526bda Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 05:17:43 -0400 Subject: [PATCH 1/9] live rooms: AI participants are marked in their grant, and chat and reactions state their origin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit live-events §4.4 and §4.5: a chat and a reaction may state an origin after their last field (1 human, 2 machine, 3 machine-reviewed, with the model's digest for 2 and 3), as a caption does. live-rooms §4.4: the grant attribute `ai` names the model that acts for an AI participant; §9.2 step 6 refuses its chat, reaction or caption that does not state origin 2 or 3 with that model (`ai_unmarked`), and a relay does not forward one. Both corpora gain vectors (additions only); both second implementations read the new fields. Co-Authored-By: Claude Opus 5.5 --- wai/extensions/live-events.md | 18 +- wai/extensions/live-rooms.md | 26 +- wai/live-conformance/events/vectors.json | 59 +++ wai/live-conformance/live_events_verify.py | 15 + wai/live-conformance/live_rooms_verify.py | 21 +- wai/live-conformance/rooms/vectors.json | 482 +++++++++++++++++++++ wai/wai-rs/src/capture_take/corpus.rs | 2 +- wai/wai-rs/src/live_ads/corpus.rs | 2 +- wai/wai-rs/src/live_compose/corpus.rs | 4 +- wai/wai-rs/src/live_events.rs | 88 +++- wai/wai-rs/src/live_events/corpus.rs | 29 +- wai/wai-rs/src/live_metering/corpus.rs | 4 +- wai/wai-rs/src/live_moderation/corpus.rs | 4 +- wai/wai-rs/src/live_record/corpus.rs | 2 +- wai/wai-rs/src/live_room.rs | 54 ++- wai/wai-rs/src/live_room/corpus.rs | 48 +- wai/wai-rs/tests/live_room.rs | 2 +- 17 files changed, 826 insertions(+), 34 deletions(-) diff --git a/wai/extensions/live-events.md b/wai/extensions/live-events.md index 0bb625358..7238ec7b5 100644 --- a/wai/extensions/live-events.md +++ b/wai/extensions/live-events.md @@ -176,7 +176,17 @@ producer **MUST NOT** write a summary its message contradicts. ### 4.4 `chat` -One text field: the message. +| field | size | meaning | +|---|---|---| +| `text` | text | the message | +| `origin` | u8 | optional: 1 human, 2 machine, 3 machine-reviewed | +| `model` | 32 | only when `origin` is 2 or 3: a digest naming the model that produced the message | + +`origin` is present exactly when a byte follows `text`; a chat without it states no origin. +`origin` and `model` mean what a caption's mean (§4.10): they are the message's +machine-generation disclosure, and a sink that labels machine-generated messages takes the +label from them. A room may require them of a principal: [live-rooms](live-rooms.md) §4.4 +marks an AI participant, whose every chat states origin 2 or 3. ### 4.5 `reaction` @@ -186,6 +196,8 @@ One text field: the message. | `value` | u16 | not 0: for emoji and raise, that draft's §4.5 integer encodings; for the others, the application's | | `flags` | u8 | bit 0: a target follows; bit 1: this retracts the reaction it repeats; every other bit 0 | | `target` | 32 | only when bit 0 is set: the digest of the event reacted to | +| `origin` | u8 | optional, as a chat's (§4.4): present exactly when a byte follows the field before it | +| `model` | 32 | only when `origin` is 2 or 3 | A reaction without a target is **standalone**. Standalone reactions are held to the policy's reaction rate (§6.3), as that draft holds them to one per second per device @@ -633,7 +645,9 @@ In a sealed package the events are one component, which forgeries that a lenient Ed25519 verifier accepts, a broken chain, a reorder, a replayed `seq`, `t` going back, an oversize payload, a kind not permitted, both rates exceeded, invalid UTF-8 and overlong text, truncated and malformed records, an unknown version and - kind, the wrong room, malformed payloads and a stated tally the replay does not reach. + kind, the wrong room, malformed payloads (a chat stating a machine origin without its + model among them) and a stated tally the replay does not reach. Chats and a reaction + stating each origin have an accepted vector. - **policy cases**: policy records, read (§5) or refused with their reason. - **binding cases**: tags, boxes and MSF records, read back to their record (§8) or refused with their reason. diff --git a/wai/extensions/live-rooms.md b/wai/extensions/live-rooms.md index af4699bdb..d1b8bd7b2 100644 --- a/wai/extensions/live-rooms.md +++ b/wai/extensions/live-rooms.md @@ -276,6 +276,20 @@ device captures for, as 64 lowercase hexadecimal digits of its key; and `minor` or `false`), whether the authority marks that principal a minor. Attributes are signed with the grant, so a layout computed from them is computed from what the authority stated. +One more attribute marks an **AI participant**: `ai`, the digest naming the model that acts +for the principal (a prior's digest, SPEC §3.1), as 64 lowercase hexadecimal digits. A grant +whose `ai` is not that form breaks this section's rule (§4.3 step 5, `grant_invalid`). The +authority states it, as it states `minor`; a principal is an AI participant from the instant +a grant carrying it is its grant in effect until one without it is. An AI participant's +`chat`, `reaction` and `caption` events state that a machine produced them, with that model +(§9.2 step 6), and the tracks it publishes are synthesized media +([synthesis](synthesis.md) §8). A relay that forwards a room's events **MUST NOT** forward +one that §9.2 step 6 refuses. A sink **MUST** show, with every presence, voice, avatar and +message of an AI participant, that it is an AI system: the information Regulation (EU) +2024/1689 Article 50(1) asks a system that interacts with people to give. The sink's showing +it is an engineering requirement of this extension, and is not a statement about the +Regulation. + ### 4.5 How a receipt names a grant A receipt that names the grant it was carried under does so with the grant reference @@ -598,6 +612,10 @@ record, in the merged order: 5. A `control` event of such a principal needs a power of its grant in effect (§3.3): `record_start` and `record_stop` the record power, any other command the direct power. Otherwise `power_not_granted`. +6. A `chat`, `reaction` or `caption` event of a principal whose grant in effect carries + `ai` (§4.4) states origin 2 (machine) or 3 (machine-reviewed), with the model `ai` + names (live-events §4.4, §4.5, §4.10). One that states no origin, origin 1, or another + model is `ai_unmarked`. Each principal's effective policy at `t` is so the policy of its grant in effect at `t`, and the room keys' is the authority's. Each record is reported with the grant it was @@ -904,6 +922,7 @@ The codes this extension adds, each with where it is raised: | `participant_limit` | §3.5, §7.2 `join` | | `outside_grant_window` | §4.3, §7.2, §9.2 step 4 | | `power_not_granted` | §9.2 step 5 | +| `ai_unmarked` | §4.4, §9.2 step 6 | | `publish_not_permitted` | §7.2 `publish`, `grant_exchange`, `takeover` | | `profile_not_granted` | §7.2 `publish`, `grant_exchange`, `takeover` | | `already_published` | §7.2 `publish` | @@ -953,8 +972,11 @@ key-log head or none, and chains, with the outcome §9.4 gives them. - **Gap**: a chain with a record not delivered. - **Refused**: each reason of §11 at least once, each vector a control of one defect, and the codes of live-events §9 that a descriptor, a grant or a record of a room raises; - among them a grant with the seek power whose role lacks it, and a grant and a role with - a power above bit 4. + among them a grant with the seek power whose role lacks it, a grant and a role with + a power above bit 4, a grant whose `ai` attribute does not read, and an AI participant's + chat that states no origin, one that states origin 1 and one that names another model. +- **AI participant**: a host whose grant names the model that acts for it, its chat and + its caption stating that model, beside a person whose chat and reaction state no origin. - **Proofs** (§10.5), in the file's `proofs`: a grant of a room presented with a proof over MOQT (verb `SETUP`) and over HTTP (a WHIP `POST` and a `DELETE`, in their header forms), each accepted; and, each refused with its reason, a grant presented with no proof, a diff --git a/wai/live-conformance/events/vectors.json b/wai/live-conformance/events/vectors.json index 10c8fcd9c..80a48e7a2 100644 --- a/wai/live-conformance/events/vectors.json +++ b/wai/live-conformance/events/vectors.json @@ -432,6 +432,52 @@ } ] }, + { + "name": "kind_chat_origin", + "about": "chat and reaction stating their origin (§4.4, §4.5): a machine message with its model's digest, a machine-reviewed one, a human one stating origin 1, and a machine reaction targeting the first", + "room": "main", + "default_policy": "default", + "policies": {}, + "events": [ + "5745563101994ce44cd8c3fa2781c7976335330a5e718019657440cb46a77db3210900ce31c2a1e8c1326f0811f77d5ef3628991d76af48f4a089e7ed08541846e351fce930000000000000000000000000000000000000000000000000000000000000000000000000000000040420f00000000000003480025004865726520697320612073756d6d617279206f66207468652074616c6b20736f206661722e02b16dcccea3b663fa41a0b4c59228cb2dddbbf2510df387ed330dc4a11aeeae1cd1c8363bda75e23afcaba414d396c3dcdeaf82c1f2954261ccb7d20aa5a527d65cf9a59fb50c9992e1afe2b51a87df6093dc255bddd3ca137bdef58caecf160c", + "5745563101994ce44cd8c3fa2781c7976335330a5e718019657440cb46a77db3210900ce31c2a1e8c1326f0811f77d5ef3628991d76af48f4a089e7ed08541846e351fce9301000000000000002ff65886fa1f94c8cd7c18a84d052b6f8e1d32c36f975bbc8f23048fef770f3580841e000000000000034b00280052657669657765643a207468652073756d6d6172792061626f76652069732061636375726174652e03b16dcccea3b663fa41a0b4c59228cb2dddbbf2510df387ed330dc4a11aeeae1c74553836741ceae7553cc599849aa68d35290a1e831a027c82a9a6def82b8f28dd6024eed3368c80a22f37559328e881733901207ba09ea7a2a9a1f55acb5607", + "5745563101994ce44cd8c3fa2781c7976335330a5e718019657440cb46a77db3210900ce31c2a1e8c1326f0811f77d5ef3628991d76af48f4a089e7ed08541846e351fce9302000000000000007bcf54fed4a5769d4c7987599718e6f2359e9f6fb17cf2eb754b6b79f034c116c0c62d00000000000003150012005479706564206279206120706572736f6e2e012df40b7fb2b654619a6d2b19133102bcbdb6b7bb5bc936a9bde2a6924842041a561080468342519243994d6e34b466c8b380ac4da5a5454739460eb48e6a8d06", + "5745563101994ce44cd8c3fa2781c7976335330a5e718019657440cb46a77db3210900ce31c2a1e8c1326f0811f77d5ef3628991d76af48f4a089e7ed08541846e351fce930300000000000000a806a8ec963a5a2bea734fd5fbdaeb68eb8890a7b1ac0e67ed8106decbe90bf800093d000000000000044500010100012ff65886fa1f94c8cd7c18a84d052b6f8e1d32c36f975bbc8f23048fef770f3502b16dcccea3b663fa41a0b4c59228cb2dddbbf2510df387ed330dc4a11aeeae1c06d90f8c5c3615fed522ab121c32ad98b740c874c0ebd1b391f55162d1a0ecec81fa90563807f382142d37ee1868d4107291c9456fa8bdd8630d0ca8f9b69f07" + ], + "outcome": "accept", + "digests": [ + "2ff65886fa1f94c8cd7c18a84d052b6f8e1d32c36f975bbc8f23048fef770f35", + "7bcf54fed4a5769d4c7987599718e6f2359e9f6fb17cf2eb754b6b79f034c116", + "a806a8ec963a5a2bea734fd5fbdaeb68eb8890a7b1ac0e67ed8106decbe90bf8", + "e262cac60f3b23c92d7e3f64aa7af8725c7f8199f5fc3588051b46a32e997a9c" + ], + "tally": { + "state_hash": "771b30268d4b5a93046d9e87dbbc10bae6fc76c4c2d5dde9071f801bfdb9a68d", + "polls": [] + }, + "bindings": [ + { + "msf": "{\"t\":1792108801000,\"data\":{\"wev1\":\"V0VWMQGZTORM2MP6J4HHl2M1MwpecYAZZXRAy0anfbMhCQDOMcKh6MEybwgR931e82KJkddq9I9KCJ5-0IVBhG41H86TAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBCDwAAAAAAAANIACUASGVyZSBpcyBhIHN1bW1hcnkgb2YgdGhlIHRhbGsgc28gZmFyLgKxbczOo7Zj-kGgtMWSKMst3bvyUQ3zh-0zDcShGu6uHNHINjvadeI6_KukFNOWw9zer4LB8pVCYcy30gqlpSfWXPmln7UMmZLhr-K1GoffYJPcJVvd08oTe971jK7PFgw\"}}", + "daterange": "#EXT-X-DATERANGE:ID=\"2ff65886fa1f94c8cd7c18a84d052b6f8e1d32c36f975bbc8f23048fef770f35\",CLASS=\"science.transaction.wai.event\",START-DATE=\"2026-10-16T00:00:01.000Z\",X-WAI-EVENT=\"V0VWMQGZTORM2MP6J4HHl2M1MwpecYAZZXRAy0anfbMhCQDOMcKh6MEybwgR931e82KJkddq9I9KCJ5-0IVBhG41H86TAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBCDwAAAAAAAANIACUASGVyZSBpcyBhIHN1bW1hcnkgb2YgdGhlIHRhbGsgc28gZmFyLgKxbczOo7Zj-kGgtMWSKMst3bvyUQ3zh-0zDcShGu6uHNHINjvadeI6_KukFNOWw9zer4LB8pVCYcy30gqlpSfWXPmln7UMmZLhr-K1GoffYJPcJVvd08oTe971jK7PFgw\"", + "emsg": "00000136656d736701000000000003e80000000000002af8000000002ff6588675726e3a7761693a6576656e743a310063686174005745563101994ce44cd8c3fa2781c7976335330a5e718019657440cb46a77db3210900ce31c2a1e8c1326f0811f77d5ef3628991d76af48f4a089e7ed08541846e351fce930000000000000000000000000000000000000000000000000000000000000000000000000000000040420f00000000000003480025004865726520697320612073756d6d617279206f66207468652074616c6b20736f206661722e02b16dcccea3b663fa41a0b4c59228cb2dddbbf2510df387ed330dc4a11aeeae1cd1c8363bda75e23afcaba414d396c3dcdeaf82c1f2954261ccb7d20aa5a527d65cf9a59fb50c9992e1afe2b51a87df6093dc255bddd3ca137bdef58caecf160c" + }, + { + "msf": "{\"t\":1792108802000,\"data\":{\"wev1\":\"V0VWMQGZTORM2MP6J4HHl2M1MwpecYAZZXRAy0anfbMhCQDOMcKh6MEybwgR931e82KJkddq9I9KCJ5-0IVBhG41H86TAQAAAAAAAAAv9liG-h-UyM18GKhNBStvjh0yw2-XW7yPIwSP73cPNYCEHgAAAAAAAANLACgAUmV2aWV3ZWQ6IHRoZSBzdW1tYXJ5IGFib3ZlIGlzIGFjY3VyYXRlLgOxbczOo7Zj-kGgtMWSKMst3bvyUQ3zh-0zDcShGu6uHHRVODZ0HOrnVTzFmYSapo01KQoegxoCfIKppt74K48o3WAk7tM2jICiLzdVkyjogXM5ASB7oJ6noqmh9VrLVgc\"}}", + "daterange": "#EXT-X-DATERANGE:ID=\"7bcf54fed4a5769d4c7987599718e6f2359e9f6fb17cf2eb754b6b79f034c116\",CLASS=\"science.transaction.wai.event\",START-DATE=\"2026-10-16T00:00:02.000Z\",X-WAI-EVENT=\"V0VWMQGZTORM2MP6J4HHl2M1MwpecYAZZXRAy0anfbMhCQDOMcKh6MEybwgR931e82KJkddq9I9KCJ5-0IVBhG41H86TAQAAAAAAAAAv9liG-h-UyM18GKhNBStvjh0yw2-XW7yPIwSP73cPNYCEHgAAAAAAAANLACgAUmV2aWV3ZWQ6IHRoZSBzdW1tYXJ5IGFib3ZlIGlzIGFjY3VyYXRlLgOxbczOo7Zj-kGgtMWSKMst3bvyUQ3zh-0zDcShGu6uHHRVODZ0HOrnVTzFmYSapo01KQoegxoCfIKppt74K48o3WAk7tM2jICiLzdVkyjogXM5ASB7oJ6noqmh9VrLVgc\"", + "emsg": "00000139656d736701000000000003e80000000000002ee0000000007bcf54fe75726e3a7761693a6576656e743a310063686174005745563101994ce44cd8c3fa2781c7976335330a5e718019657440cb46a77db3210900ce31c2a1e8c1326f0811f77d5ef3628991d76af48f4a089e7ed08541846e351fce9301000000000000002ff65886fa1f94c8cd7c18a84d052b6f8e1d32c36f975bbc8f23048fef770f3580841e000000000000034b00280052657669657765643a207468652073756d6d6172792061626f76652069732061636375726174652e03b16dcccea3b663fa41a0b4c59228cb2dddbbf2510df387ed330dc4a11aeeae1c74553836741ceae7553cc599849aa68d35290a1e831a027c82a9a6def82b8f28dd6024eed3368c80a22f37559328e881733901207ba09ea7a2a9a1f55acb5607" + }, + { + "msf": "{\"t\":1792108803000,\"data\":{\"wev1\":\"V0VWMQGZTORM2MP6J4HHl2M1MwpecYAZZXRAy0anfbMhCQDOMcKh6MEybwgR931e82KJkddq9I9KCJ5-0IVBhG41H86TAgAAAAAAAAB7z1T-1KV2nUx5h1mXGObyNZ6fb7F88ut1S2t58DTBFsDGLQAAAAAAAAMVABIAVHlwZWQgYnkgYSBwZXJzb24uAS30C3-ytlRhmm0rGRMxAry9tre7W8k2qb3ippJIQgQaVhCARoNCUZJDmU1uNLRmyLOArE2lpUVHOUYOtI5qjQY\"}}", + "daterange": "#EXT-X-DATERANGE:ID=\"a806a8ec963a5a2bea734fd5fbdaeb68eb8890a7b1ac0e67ed8106decbe90bf8\",CLASS=\"science.transaction.wai.event\",START-DATE=\"2026-10-16T00:00:03.000Z\",X-WAI-EVENT=\"V0VWMQGZTORM2MP6J4HHl2M1MwpecYAZZXRAy0anfbMhCQDOMcKh6MEybwgR931e82KJkddq9I9KCJ5-0IVBhG41H86TAgAAAAAAAAB7z1T-1KV2nUx5h1mXGObyNZ6fb7F88ut1S2t58DTBFsDGLQAAAAAAAAMVABIAVHlwZWQgYnkgYSBwZXJzb24uAS30C3-ytlRhmm0rGRMxAry9tre7W8k2qb3ippJIQgQaVhCARoNCUZJDmU1uNLRmyLOArE2lpUVHOUYOtI5qjQY\"", + "emsg": "00000103656d736701000000000003e800000000000032c800000000a806a8ec75726e3a7761693a6576656e743a310063686174005745563101994ce44cd8c3fa2781c7976335330a5e718019657440cb46a77db3210900ce31c2a1e8c1326f0811f77d5ef3628991d76af48f4a089e7ed08541846e351fce9302000000000000007bcf54fed4a5769d4c7987599718e6f2359e9f6fb17cf2eb754b6b79f034c116c0c62d00000000000003150012005479706564206279206120706572736f6e2e012df40b7fb2b654619a6d2b19133102bcbdb6b7bb5bc936a9bde2a6924842041a561080468342519243994d6e34b466c8b380ac4da5a5454739460eb48e6a8d06" + }, + { + "msf": "{\"t\":1792108804000,\"data\":{\"wev1\":\"V0VWMQGZTORM2MP6J4HHl2M1MwpecYAZZXRAy0anfbMhCQDOMcKh6MEybwgR931e82KJkddq9I9KCJ5-0IVBhG41H86TAwAAAAAAAACoBqjsljpaK-pzT9X72uto64iQp7GsDmftgQbey-kL-AAJPQAAAAAAAARFAAEBAAEv9liG-h-UyM18GKhNBStvjh0yw2-XW7yPIwSP73cPNQKxbczOo7Zj-kGgtMWSKMst3bvyUQ3zh-0zDcShGu6uHAbZD4xcNhX-1SKrEhwyrZi3QMh0wOvRs5H1UWLRoOzsgfqQVjgH84IULTfuGGjUEHKRyUVvqL3YYw0MqPm2nwc\"}}", + "daterange": "#EXT-X-DATERANGE:ID=\"e262cac60f3b23c92d7e3f64aa7af8725c7f8199f5fc3588051b46a32e997a9c\",CLASS=\"science.transaction.wai.event\",START-DATE=\"2026-10-16T00:00:04.000Z\",X-WAI-EVENT=\"V0VWMQGZTORM2MP6J4HHl2M1MwpecYAZZXRAy0anfbMhCQDOMcKh6MEybwgR931e82KJkddq9I9KCJ5-0IVBhG41H86TAwAAAAAAAACoBqjsljpaK-pzT9X72uto64iQp7GsDmftgQbey-kL-AAJPQAAAAAAAARFAAEBAAEv9liG-h-UyM18GKhNBStvjh0yw2-XW7yPIwSP73cPNQKxbczOo7Zj-kGgtMWSKMst3bvyUQ3zh-0zDcShGu6uHAbZD4xcNhX-1SKrEhwyrZi3QMh0wOvRs5H1UWLRoOzsgfqQVjgH84IULTfuGGjUEHKRyUVvqL3YYw0MqPm2nwc\"", + "emsg": "00000137656d736701000000000003e800000000000036b000000000e262cac675726e3a7761693a6576656e743a31007265616374696f6e005745563101994ce44cd8c3fa2781c7976335330a5e718019657440cb46a77db3210900ce31c2a1e8c1326f0811f77d5ef3628991d76af48f4a089e7ed08541846e351fce930300000000000000a806a8ec963a5a2bea734fd5fbdaeb68eb8890a7b1ac0e67ed8106decbe90bf800093d000000000000044500010100012ff65886fa1f94c8cd7c18a84d052b6f8e1d32c36f975bbc8f23048fef770f3502b16dcccea3b663fa41a0b4c59228cb2dddbbf2510df387ed330dc4a11aeeae1c06d90f8c5c3615fed522ab121c32ad98b740c874c0ebd1b391f55162d1a0ecec81fa90563807f382142d37ee1868d4107291c9456fa8bdd8630d0ca8f9b69f07" + } + ] + }, { "name": "kind_caption", "about": "caption (§4.10): human, machine and machine-reviewed origins, the latter two with a model digest, three language tags, and a caption with no stated duration", @@ -1420,6 +1466,19 @@ "reason": "malformed_payload", "index": 0 }, + { + "name": "malformed_payload_origin", + "about": "a chat stating origin 2 with no model digest after it (§4.4)", + "room": "main", + "default_policy": "default", + "policies": {}, + "events": [ + "5745563101994ce44cd8c3fa2781c7976335330a5e718019657440cb46a77db3210900ce31c2a1e8c1326f0811f77d5ef3628991d76af48f4a089e7ed08541846e351fce930000000000000000000000000000000000000000000000000000000000000000000000000000000040420f000000000000030f000c007768696368206d6f64656c3f02b8bbfca6c533438f2f05fdd56a1354fe50609951f58fef3cd0087f8bb25afe82d87dc1114046480dc6a6a18df663a91b25172f1c6c356084519d478b6107f207" + ], + "outcome": "refuse", + "reason": "malformed_payload", + "index": 0 + }, { "name": "malformed_payload_poll", "about": "a poll_open with one option (§4.6)", diff --git a/wai/live-conformance/live_events_verify.py b/wai/live-conformance/live_events_verify.py index f681fe30a..ff38b50fb 100644 --- a/wai/live-conformance/live_events_verify.py +++ b/wai/live-conformance/live_events_verify.py @@ -164,6 +164,19 @@ def splice_info_section_ok(b): return len(b) >= 3 and b[0] == 0xFC and 3 + (((b[1] & 0x0F) << 8) | b[2]) == len(b) +def stated_origin(r): + """§4.4, §4.5: the origin a chat or a reaction may state after its last field, as + (origin, model), or (None, None) when no byte follows.""" + if r.done(): + return None, None + o = r.u8() + if o == 1: + return 1, None + if o in (2, 3): + return o, r.take(32) + raise Refuse("malformed_payload") + + def read_payload(kind, b, max_cp): """§4: the payload as a dict, or Refuse.""" m = "malformed_payload" @@ -188,6 +201,7 @@ def read_payload(kind, b, max_cp): raise Refuse(m) elif kind == 0x03: p["text"] = text(r, max_cp) + p["origin"], p["model"] = stated_origin(r) elif kind == 0x04: p["type"] = r.u8() if p["type"] not in (1, 2, 3, 4, 5): @@ -200,6 +214,7 @@ def read_payload(kind, b, max_cp): raise Refuse(m) p["target"] = r.take(32) if flags & 1 else None p["remove"] = bool(flags & 2) + p["origin"], p["model"] = stated_origin(r) elif kind == 0x05: p["question"] = text(r, max_cp) n = r.u8() diff --git a/wai/live-conformance/live_rooms_verify.py b/wai/live-conformance/live_rooms_verify.py index 1a3834268..63ee99a0f 100644 --- a/wai/live-conformance/live_rooms_verify.py +++ b/wai/live-conformance/live_rooms_verify.py @@ -73,7 +73,7 @@ REASONS = [ "grant_exceeds_room", "exchange_unlinked", "exchange_mismatch", "lifecycle_malformed", "grant_unknown", "grant_wrong_principal", "grant_revoked", "grant_reused", "already_present", "not_present", "participant_limit", "outside_grant_window", - "power_not_granted", "publish_not_permitted", "profile_not_granted", "already_published", + "power_not_granted", "ai_unmarked", "publish_not_permitted", "profile_not_granted", "already_published", "not_published", "slot_unknown", "slot_full", "publisher_limit", "exchange_not_current", "exchange_off_tick", "takeover_not_higher", "failover_not_standby", "failover_early", "pop_missing", "pop_malformed", "pop_wrong_grant", "pop_wrong_key", "pop_bad_signature", @@ -395,11 +395,22 @@ def grant_fields_ok(g, d, held): media.add(m) if len(g["origins"]) > 8 or not all(is_token(o, 255) for o in g["origins"]): return False - if read_attrs(g["attrs"]) is None: + attrs = read_attrs(g["attrs"]) + if attrs is None: + return False + if any(k == "ai" and not re.fullmatch(r"[0-9a-f]{64}", v) for k, v in attrs): return False return all(h["digest"] != g["digest"] for h in held) +def ai_model(g): + """§4.4: the model an AI participant's grant names, as bytes, or None.""" + for k, v in read_attrs(g["attrs"]) or []: + if k == "ai": + return bytes.fromhex(v) + return None + + def publishes(g, m): return m in MEDIA and g["publish"] >> (m - 1) & 1 == 1 @@ -1035,6 +1046,10 @@ def step(d, room, ver, seen, auth, b): need = RECORD if payload["command"] in (1, 2) else DIRECT if g["powers"] & need == 0: raise Refuse("power_not_granted") + model = ai_model(g) + if model is not None and payload["kind"] in (0x03, 0x04, 0x0A): + if payload["origin"] not in (2, 3) or payload["model"] != model: + raise Refuse("ai_unmarked") return g["digest"] @@ -1124,7 +1139,7 @@ def main(corpus_dir): missing = [r for r in REASONS if r not in reasons_seen] if missing: fails.append("no vector refused with %s" % ", ".join(missing)) - rooms = ["lesson", "broadcast", "takeover", "redundant_failover", "promote_viewer", "dvr"] + rooms = ["lesson", "broadcast", "takeover", "redundant_failover", "promote_viewer", "dvr", "ai_participant"] absent = [r for r in rooms if outcomes.get(r) != "accept"] if absent: fails.append("rooms not accepted: %s" % ", ".join(absent)) diff --git a/wai/live-conformance/rooms/vectors.json b/wai/live-conformance/rooms/vectors.json index 5c22690ff..3b543d184 100644 --- a/wai/live-conformance/rooms/vectors.json +++ b/wai/live-conformance/rooms/vectors.json @@ -3529,6 +3529,23 @@ 1 ] }, + { + "name": "grant_ai_malformed", + "about": "a grant whose ai attribute is not 64 lowercase hexadecimal digits (§4.4)", + "descriptor": "5752443101988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b00406cd8e95d060040420f0000000000150068747470733a2f2f726f6f6d732e6578616d706c650800000003003c0000005745503101fe1f0000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f00000000000504686f737407010102ae06000000000000000000000000000000000000000000000000000000000000096d6f64657261746f720001000108080000000000000000000000000000000000000000000000000000000000000861756469656e636500010100580000000000000000000000000000000000000000000000000000000000000005677565737403010100580100000000000000000000000000000000000000000000000000000000000007656e636f64657203000000000000000000000000000000000000000000000000000000000000000000000002057374616765020100000000046d61696e0202d007000002067370656563680101046f707573000001000000000000100000004000000080bb0000010f0a63616d6572612d37323002020d617630312e302e30354d2e30380b617663312e363430303146010002800268011e0096000000f40100000005d0021e00f4010000dc0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086c97e1a4b2609cb3438fd6c165e0bdc6f016bae7d16c4d37a387f4be3891b46f97c63fbf4814de11aadb3ee409d246a182017b43cacb6a6c64905bceb31c30b", + "grants": [ + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa100000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a0000000000070101020000000000000000000000000000000000000000000000000000000000000000005745503101ae060000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c65000008067f81349126761a6b7fe0cf3bd66d94800e6a65dc2f9d1d07de1a362f6d95bd8122dad96eed33117981aa9923f907d8a1a236fcf8c14579e586cbe500470b", + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c00000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000200010100000000000000000000000000000000000000000000000000000000000000000000574550310158000000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c6545000261694000414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141416e3520d47ed02cd4fa26c68cb2a874282c9d370a49429fd985bb64ecbca67ceaf7563a898af9d9091047cd32ad068802707bec6f1820c82b6f0ec86d65ca820b" + ], + "key_log_head": "{\"grant_max_s\":86400,\"issued_at\":\"2026-10-15T00:00:00Z\",\"keys\":[{\"compromised_since\":null,\"id\":\"room-authority\",\"key\":\"988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285\",\"may_sign\":[\"live/room\"],\"status\":\"active\",\"until\":null}],\"kind\":\"wai-key-set\",\"next_root\":\"3328656f3df5d9617e108d46080e23610712c9979d4437e706240f53d6cadf7b\",\"origin\":\"https://rooms.example\",\"prev\":null,\"revoked_modules\":[],\"root\":\"687972706312d9675734bf13b4bcf695ecb1b374de7809242aa91f130c6a1c0c\",\"seq\":0,\"sig\":\"ba55b157ebe88fc97f2b5a5e3556415563552ed88b9931060b9e99ae0a7decefdc63e2cb916b9a5d03c80a0c1823de14e7d8d3fe0b726c2464d823611780290a\",\"status_max_age_s\":3600}", + "chains": [], + "outcome": "refuse", + "reason": "grant_invalid", + "at": [ + "grant", + 1 + ] + }, { "name": "grant_attrs_too_long", "about": "a grant whose attributes take more than 1024 bytes (§4.4)", @@ -4489,6 +4506,471 @@ 1 ] }, + { + "name": "ai_participant", + "about": "a host whose grant's ai attribute names a model: its chat states origin 2 and its caption origin 3, each with that model; a person's chat and reaction state none (§4.4, §9.2 step 6)", + "descriptor": "5752443101988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b00406cd8e95d060040420f0000000000150068747470733a2f2f726f6f6d732e6578616d706c650800000003003c0000005745503101fe1f0000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f00000000000504686f737407010102ae06000000000000000000000000000000000000000000000000000000000000096d6f64657261746f720001000108080000000000000000000000000000000000000000000000000000000000000861756469656e636500010100580000000000000000000000000000000000000000000000000000000000000005677565737403010100580100000000000000000000000000000000000000000000000000000000000007656e636f64657203000000000000000000000000000000000000000000000000000000000000000000000002057374616765020100000000046d61696e0202d007000002067370656563680101046f707573000001000000000000100000004000000080bb0000010f0a63616d6572612d37323002020d617630312e302e30354d2e30380b617663312e363430303146010002800268011e0096000000f40100000005d0021e00f4010000dc0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086c97e1a4b2609cb3438fd6c165e0bdc6f016bae7d16c4d37a387f4be3891b46f97c63fbf4814de11aadb3ee409d246a182017b43cacb6a6c64905bceb31c30b", + "grants": [ + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa100000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a0000000000070101020000000000000000000000000000000000000000000000000000000000000000005745503101ae060000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c6545000261694000383763386330653335666337666136613964346231393232336634303238326663333565396665633265353933383061633562336466616561653664353930385c245e4d368eea2fa46a37c069226e63a3489e51338486b574f405737169de74e0e230e5f27b523eb35cdc5e5a63fe4f104c48556ba46d8433b3db9a265eaa01", + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c00000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000200010100000000000000000000000000000000000000000000000000000000000000000000574550310158000000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c650000da52cb8a9a33184a5da85c37affe7d9dfc6f4e7a3019ecc07c090358ded448066ac88859718562bb805f34d7da43880addb6098e654e8e51481afb9ee22ee40e" + ], + "key_log_head": "{\"grant_max_s\":86400,\"issued_at\":\"2026-10-15T00:00:00Z\",\"keys\":[{\"compromised_since\":null,\"id\":\"room-authority\",\"key\":\"988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285\",\"may_sign\":[\"live/room\"],\"status\":\"active\",\"until\":null}],\"kind\":\"wai-key-set\",\"next_root\":\"3328656f3df5d9617e108d46080e23610712c9979d4437e706240f53d6cadf7b\",\"origin\":\"https://rooms.example\",\"prev\":null,\"revoked_modules\":[],\"root\":\"687972706312d9675734bf13b4bcf695ecb1b374de7809242aa91f130c6a1c0c\",\"seq\":0,\"sig\":\"ba55b157ebe88fc97f2b5a5e3556415563552ed88b9931060b9e99ae0a7decefdc63e2cb916b9a5d03c80a0c1823de14e7d8d3fe0b726c2464d823611780290a\",\"status_max_age_s\":3600}", + "chains": [ + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040002df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa13427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f9654022ff7403111b077255cc79fffeb37146f44c32b5f5f811ee202b6a1811fc80d874544e4e887d493655b3d5f1e26a16664ec23594e8e685b4201ff3d8c9e105", + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b010000000000000066e0e16b1f2817739e3b6f2c9c87086b31f86249123017fc159142eca323af28000000000000000000104000fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a38607dad484d1fc66d48a45d08c1cec17d18889212fe90f204ee16684d701e5ebf4cb837bdd6dd0af53318a5465ac6f44a98f3769e7481e99169975af532a5614809" + ], + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c0000000000000000000000000000000000000000000000000000000000000000000000000000000040420f0000000000000312001000497320616e796f6e652074686572653f882e98ec64ee02bb24da475a9dc9c491bb077b9fbd4cd32c90c73a253d1848e72c394c8a3fe09a0c620fcb5ad17a38d15b96620fd504be6c7dd57f25a889e20a", + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c010000000000000021509022cbf78272fd37df1a2ac940fddcacf6b7c394169b54e256abac734c8200093d000000000000040400040100001557491f18f4601eba695fd96f943ad21fe5e87575fe87a3824c41e81a2e99fd47231c68b03c133f8dd6a33001fe1fa77671adaf576efa3dad5ae35488ac6508" + ], + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa10000000000000000000000000000000000000000000000000000000000000000000000000000000080841e000000000000034300200057656c636f6d652e204920616d20616e206175746f6d6174656420686f73742e0287c8c0e35fc7fa6a9d4b19223f40282fc35e9fec2e59380ac5b3dfaeae6d590818cbffb02f3b3c5c74d381e071119923078b88c258f5dc2ed86b012166e80546d4a2111fe5e0e26a4e24428b73194386e0b4f1f731c50e47737760846cb1ea0f", + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa1010000000000000081cef7e8b20d0491664ba49ee6fbd5f26e16ec4decbcf820a3f7dedb10ac0403c0c62d0000000000000a3200d00700000387c8c0e35fc7fa6a9d4b19223f40282fc35e9fec2e59380ac5b3dfaeae6d590802656e080057656c636f6d652ebeb6999762bad005e0998b47d5b193f5e32326601e9524604b5b39d2eb25fb631cd22f64c01ab4db57ccd4a104e810387806e200748bdce6cdeb0720a7c3280f" + ] + ], + "outcome": "accept", + "room": "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "grant_digests": [ + "3427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f965", + "37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a3860" + ], + "order": [ + [ + 0, + 0, + null + ], + [ + 0, + 1, + null + ], + [ + 1, + 0, + "37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a3860" + ], + [ + 2, + 0, + "3427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f965" + ], + [ + 2, + 1, + "3427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f965" + ], + [ + 1, + 1, + "37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a3860" + ] + ], + "state": { + "present": [ + { + "principal": "2df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa1", + "grant": "3427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f965" + }, + { + "principal": "fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c", + "grant": "37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a3860" + } + ], + "published": [], + "slots": [ + { + "slot": "stage", + "media": "video", + "live": null, + "standby": null, + "precedence": null + }, + { + "slot": "main", + "media": "video", + "live": null, + "standby": null, + "precedence": null + } + ], + "tracks": [], + "timeline": [ + { + "principal": "2df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa1", + "grant": "3427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f965", + "t": 0 + }, + { + "principal": "fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c", + "grant": "37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a3860", + "t": 0 + } + ] + }, + "tally": { + "state_hash": "771b30268d4b5a93046d9e87dbbc10bae6fc76c4c2d5dde9071f801bfdb9a68d", + "polls": [] + }, + "moqt": [ + { + "grant": "3427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f965", + "reval": 60, + "seek": false, + "scopes": [ + [ + [ + 0 + ] + ], + [ + [ + 2, + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "2df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa1", + "audio", + null + ] + ], + [ + [ + 2, + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "2df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa1", + "video", + null + ] + ], + [ + [ + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "_stage", + "video", + null + ] + ], + [ + [ + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "_stage", + null + ], + "catalog" + ], + [ + [ + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "_main", + "video", + null + ] + ], + [ + [ + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "_main", + null + ], + "catalog" + ], + [ + [ + 2, + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "2df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa1", + "screen", + null + ] + ], + [ + [ + 2, + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "2df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa1", + null + ], + "catalog" + ], + [ + [ + 2, + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "2df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa1", + "events", + null + ], + "0" + ], + [ + [ + 3 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ] + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "catalog" + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "0" + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "0_receipts" + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "1" + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "1_receipts" + ] + ] + }, + { + "grant": "37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a3860", + "reval": 60, + "seek": false, + "scopes": [ + [ + [ + 0 + ] + ], + [ + [ + 2, + 6 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687", + "fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c", + "events", + null + ], + "0" + ], + [ + [ + 3 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ] + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "catalog" + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "0" + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "0_receipts" + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "1" + ], + [ + [ + 4, + 7, + 8 + ], + [ + "91ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687" + ], + "1_receipts" + ] + ] + } + ] + }, + { + "name": "ai_unmarked", + "about": "an AI participant's chat that states no origin (§9.2 step 6)", + "descriptor": "5752443101988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b00406cd8e95d060040420f0000000000150068747470733a2f2f726f6f6d732e6578616d706c650800000003003c0000005745503101fe1f0000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f00000000000504686f737407010102ae06000000000000000000000000000000000000000000000000000000000000096d6f64657261746f720001000108080000000000000000000000000000000000000000000000000000000000000861756469656e636500010100580000000000000000000000000000000000000000000000000000000000000005677565737403010100580100000000000000000000000000000000000000000000000000000000000007656e636f64657203000000000000000000000000000000000000000000000000000000000000000000000002057374616765020100000000046d61696e0202d007000002067370656563680101046f707573000001000000000000100000004000000080bb0000010f0a63616d6572612d37323002020d617630312e302e30354d2e30380b617663312e363430303146010002800268011e0096000000f40100000005d0021e00f4010000dc0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086c97e1a4b2609cb3438fd6c165e0bdc6f016bae7d16c4d37a387f4be3891b46f97c63fbf4814de11aadb3ee409d246a182017b43cacb6a6c64905bceb31c30b", + "grants": [ + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa100000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a0000000000070101020000000000000000000000000000000000000000000000000000000000000000005745503101ae060000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c6545000261694000383763386330653335666337666136613964346231393232336634303238326663333565396665633265353933383061633562336466616561653664353930385c245e4d368eea2fa46a37c069226e63a3489e51338486b574f405737169de74e0e230e5f27b523eb35cdc5e5a63fe4f104c48556ba46d8433b3db9a265eaa01", + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c00000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000200010100000000000000000000000000000000000000000000000000000000000000000000574550310158000000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c650000da52cb8a9a33184a5da85c37affe7d9dfc6f4e7a3019ecc07c090358ded448066ac88859718562bb805f34d7da43880addb6098e654e8e51481afb9ee22ee40e" + ], + "key_log_head": "{\"grant_max_s\":86400,\"issued_at\":\"2026-10-15T00:00:00Z\",\"keys\":[{\"compromised_since\":null,\"id\":\"room-authority\",\"key\":\"988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285\",\"may_sign\":[\"live/room\"],\"status\":\"active\",\"until\":null}],\"kind\":\"wai-key-set\",\"next_root\":\"3328656f3df5d9617e108d46080e23610712c9979d4437e706240f53d6cadf7b\",\"origin\":\"https://rooms.example\",\"prev\":null,\"revoked_modules\":[],\"root\":\"687972706312d9675734bf13b4bcf695ecb1b374de7809242aa91f130c6a1c0c\",\"seq\":0,\"sig\":\"ba55b157ebe88fc97f2b5a5e3556415563552ed88b9931060b9e99ae0a7decefdc63e2cb916b9a5d03c80a0c1823de14e7d8d3fe0b726c2464d823611780290a\",\"status_max_age_s\":3600}", + "chains": [ + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040002df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa13427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f9654022ff7403111b077255cc79fffeb37146f44c32b5f5f811ee202b6a1811fc80d874544e4e887d493655b3d5f1e26a16664ec23594e8e685b4201ff3d8c9e105", + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b010000000000000066e0e16b1f2817739e3b6f2c9c87086b31f86249123017fc159142eca323af28000000000000000000104000fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a38607dad484d1fc66d48a45d08c1cec17d18889212fe90f204ee16684d701e5ebf4cb837bdd6dd0af53318a5465ac6f44a98f3769e7481e99169975af532a5614809" + ], + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c0000000000000000000000000000000000000000000000000000000000000000000000000000000040420f0000000000000312001000497320616e796f6e652074686572653f882e98ec64ee02bb24da475a9dc9c491bb077b9fbd4cd32c90c73a253d1848e72c394c8a3fe09a0c620fcb5ad17a38d15b96620fd504be6c7dd57f25a889e20a" + ], + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa10000000000000000000000000000000000000000000000000000000000000000000000000000000080841e000000000000032200200057656c636f6d652e204920616d20616e206175746f6d6174656420686f73742ed3025193682bf2a1635fed1f8919f6b62399702536a5bbb76d1658c9f00056b7d858882840895670a8c9ea39ecd2b96d2845d5e37481852523eec2e6a1bc4b0b" + ] + ], + "outcome": "refuse", + "reason": "ai_unmarked", + "at": [ + "record", + 2, + 0 + ] + }, + { + "name": "ai_unmarked_human", + "about": "an AI participant's chat that states origin 1, human (§9.2 step 6)", + "descriptor": "5752443101988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b00406cd8e95d060040420f0000000000150068747470733a2f2f726f6f6d732e6578616d706c650800000003003c0000005745503101fe1f0000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f00000000000504686f737407010102ae06000000000000000000000000000000000000000000000000000000000000096d6f64657261746f720001000108080000000000000000000000000000000000000000000000000000000000000861756469656e636500010100580000000000000000000000000000000000000000000000000000000000000005677565737403010100580100000000000000000000000000000000000000000000000000000000000007656e636f64657203000000000000000000000000000000000000000000000000000000000000000000000002057374616765020100000000046d61696e0202d007000002067370656563680101046f707573000001000000000000100000004000000080bb0000010f0a63616d6572612d37323002020d617630312e302e30354d2e30380b617663312e363430303146010002800268011e0096000000f40100000005d0021e00f4010000dc0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086c97e1a4b2609cb3438fd6c165e0bdc6f016bae7d16c4d37a387f4be3891b46f97c63fbf4814de11aadb3ee409d246a182017b43cacb6a6c64905bceb31c30b", + "grants": [ + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa100000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a0000000000070101020000000000000000000000000000000000000000000000000000000000000000005745503101ae060000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c6545000261694000383763386330653335666337666136613964346231393232336634303238326663333565396665633265353933383061633562336466616561653664353930385c245e4d368eea2fa46a37c069226e63a3489e51338486b574f405737169de74e0e230e5f27b523eb35cdc5e5a63fe4f104c48556ba46d8433b3db9a265eaa01", + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c00000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000200010100000000000000000000000000000000000000000000000000000000000000000000574550310158000000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c650000da52cb8a9a33184a5da85c37affe7d9dfc6f4e7a3019ecc07c090358ded448066ac88859718562bb805f34d7da43880addb6098e654e8e51481afb9ee22ee40e" + ], + "key_log_head": "{\"grant_max_s\":86400,\"issued_at\":\"2026-10-15T00:00:00Z\",\"keys\":[{\"compromised_since\":null,\"id\":\"room-authority\",\"key\":\"988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285\",\"may_sign\":[\"live/room\"],\"status\":\"active\",\"until\":null}],\"kind\":\"wai-key-set\",\"next_root\":\"3328656f3df5d9617e108d46080e23610712c9979d4437e706240f53d6cadf7b\",\"origin\":\"https://rooms.example\",\"prev\":null,\"revoked_modules\":[],\"root\":\"687972706312d9675734bf13b4bcf695ecb1b374de7809242aa91f130c6a1c0c\",\"seq\":0,\"sig\":\"ba55b157ebe88fc97f2b5a5e3556415563552ed88b9931060b9e99ae0a7decefdc63e2cb916b9a5d03c80a0c1823de14e7d8d3fe0b726c2464d823611780290a\",\"status_max_age_s\":3600}", + "chains": [ + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040002df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa13427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f9654022ff7403111b077255cc79fffeb37146f44c32b5f5f811ee202b6a1811fc80d874544e4e887d493655b3d5f1e26a16664ec23594e8e685b4201ff3d8c9e105", + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b010000000000000066e0e16b1f2817739e3b6f2c9c87086b31f86249123017fc159142eca323af28000000000000000000104000fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a38607dad484d1fc66d48a45d08c1cec17d18889212fe90f204ee16684d701e5ebf4cb837bdd6dd0af53318a5465ac6f44a98f3769e7481e99169975af532a5614809" + ], + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c0000000000000000000000000000000000000000000000000000000000000000000000000000000040420f0000000000000312001000497320616e796f6e652074686572653f882e98ec64ee02bb24da475a9dc9c491bb077b9fbd4cd32c90c73a253d1848e72c394c8a3fe09a0c620fcb5ad17a38d15b96620fd504be6c7dd57f25a889e20a" + ], + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa10000000000000000000000000000000000000000000000000000000000000000000000000000000080841e000000000000032300200057656c636f6d652e204920616d20616e206175746f6d6174656420686f73742e0127f048da20a70e3abec01ff3cdf9c9563b4585ccd45c192e7d28a8ae98773910f796c7ffea6a735359c3d05699e25365dc6cad4d3d12f531758f088ada112c00" + ] + ], + "outcome": "refuse", + "reason": "ai_unmarked", + "at": [ + "record", + 2, + 0 + ] + }, + { + "name": "ai_unmarked_model", + "about": "an AI participant's chat that states origin 2 with a model other than its grant's (§9.2 step 6)", + "descriptor": "5752443101988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b00406cd8e95d060040420f0000000000150068747470733a2f2f726f6f6d732e6578616d706c650800000003003c0000005745503101fe1f0000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f00000000000504686f737407010102ae06000000000000000000000000000000000000000000000000000000000000096d6f64657261746f720001000108080000000000000000000000000000000000000000000000000000000000000861756469656e636500010100580000000000000000000000000000000000000000000000000000000000000005677565737403010100580100000000000000000000000000000000000000000000000000000000000007656e636f64657203000000000000000000000000000000000000000000000000000000000000000000000002057374616765020100000000046d61696e0202d007000002067370656563680101046f707573000001000000000000100000004000000080bb0000010f0a63616d6572612d37323002020d617630312e302e30354d2e30380b617663312e363430303146010002800268011e0096000000f40100000005d0021e00f4010000dc0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086c97e1a4b2609cb3438fd6c165e0bdc6f016bae7d16c4d37a387f4be3891b46f97c63fbf4814de11aadb3ee409d246a182017b43cacb6a6c64905bceb31c30b", + "grants": [ + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa100000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a0000000000070101020000000000000000000000000000000000000000000000000000000000000000005745503101ae060000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c6545000261694000383763386330653335666337666136613964346231393232336634303238326663333565396665633265353933383061633562336466616561653664353930385c245e4d368eea2fa46a37c069226e63a3489e51338486b574f405737169de74e0e230e5f27b523eb35cdc5e5a63fe4f104c48556ba46d8433b3db9a265eaa01", + "575247310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c00000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000200010100000000000000000000000000000000000000000000000000000000000000000000574550310158000000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c650000da52cb8a9a33184a5da85c37affe7d9dfc6f4e7a3019ecc07c090358ded448066ac88859718562bb805f34d7da43880addb6098e654e8e51481afb9ee22ee40e" + ], + "key_log_head": "{\"grant_max_s\":86400,\"issued_at\":\"2026-10-15T00:00:00Z\",\"keys\":[{\"compromised_since\":null,\"id\":\"room-authority\",\"key\":\"988ec20b60d2d23c4c475c1f4e623f115ffbe7659bd0c81c0af9233c28589285\",\"may_sign\":[\"live/room\"],\"status\":\"active\",\"until\":null}],\"kind\":\"wai-key-set\",\"next_root\":\"3328656f3df5d9617e108d46080e23610712c9979d4437e706240f53d6cadf7b\",\"origin\":\"https://rooms.example\",\"prev\":null,\"revoked_modules\":[],\"root\":\"687972706312d9675734bf13b4bcf695ecb1b374de7809242aa91f130c6a1c0c\",\"seq\":0,\"sig\":\"ba55b157ebe88fc97f2b5a5e3556415563552ed88b9931060b9e99ae0a7decefdc63e2cb916b9a5d03c80a0c1823de14e7d8d3fe0b726c2464d823611780290a\",\"status_max_age_s\":3600}", + "chains": [ + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040002df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa13427f23d6605d4ed9be070c149aa749edd535e46fb4a813e47bdd750d546f9654022ff7403111b077255cc79fffeb37146f44c32b5f5f811ee202b6a1811fc80d874544e4e887d493655b3d5f1e26a16664ec23594e8e685b4201ff3d8c9e105", + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687c73c9d1dc51b9a83c180ec0bd17f1c8520c0ab70c4874225109cadb2d493ec1b010000000000000066e0e16b1f2817739e3b6f2c9c87086b31f86249123017fc159142eca323af28000000000000000000104000fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c37d406a857ee63ae9fecd4f293211c3e92c6847beb4871066ecfbca21f4a38607dad484d1fc66d48a45d08c1cec17d18889212fe90f204ee16684d701e5ebf4cb837bdd6dd0af53318a5465ac6f44a98f3769e7481e99169975af532a5614809" + ], + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a203687fcb90cb92576cc567d08bedfa255bb94366c5b882a03ce247745dfd0e32ab09c0000000000000000000000000000000000000000000000000000000000000000000000000000000040420f0000000000000312001000497320616e796f6e652074686572653f882e98ec64ee02bb24da475a9dc9c491bb077b9fbd4cd32c90c73a253d1848e72c394c8a3fe09a0c620fcb5ad17a38d15b96620fd504be6c7dd57f25a889e20a" + ], + [ + "574556310191ac4a45ebdee375b286d920fb2d07f064a31e7b7243829126eee2e27a2036872df53b5dcc0c0ad294a87b904069ccadbde41494fa7e39cbf6720e1b2d5c5aa10000000000000000000000000000000000000000000000000000000000000000000000000000000080841e000000000000034300200057656c636f6d652e204920616d20616e206175746f6d6174656420686f73742e0267fa6ab069abc245aeccbc6afa47448ff1510522c9f9b7a79b7da263115ac1813dad0dd58243a1930f8b515255419da8042fb3bd634162e4d49a0eaa3c634dcc81144fe2cc83c3d0852e7a516af18e7f9deaa4883b4faf13af1ae9f150590302" + ] + ], + "outcome": "refuse", + "reason": "ai_unmarked", + "at": [ + "record", + 2, + 0 + ] + }, { "name": "rate_exceeded", "about": "a third event in one second under a grant of two per second (§4.1; live-events §6.3)", diff --git a/wai/wai-rs/src/capture_take/corpus.rs b/wai/wai-rs/src/capture_take/corpus.rs index cf11a70bb..3bae5f447 100644 --- a/wai/wai-rs/src/capture_take/corpus.rs +++ b/wai/wai-rs/src/capture_take/corpus.rs @@ -417,7 +417,7 @@ fn write(s: &Scene) -> Written { rm.emit(DIRECTOR, capture + k * S, &Payload::Marker(Marker { taxonomy: 4, polarity, text: note.into(), attachment: None })); } rm.emit(DIRECTOR, s.stop_t, &Payload::Control { target: pk(DEVICE), command: Command::RecordStop }); - let chat = rm.emit(VIEWER, s.start_t + 500 * MS, &Payload::Chat { text: "good luck".into() }); + let chat = rm.emit(VIEWER, s.start_t + 500 * MS, &Payload::Chat { text: "good luck".into(), origin: None }); // The chunks, as captured, as the device commits them, and as the manifest lists them. let device_time = |t: u64| (t as i128 - s.offset as i128) as u64; let bytes: Vec> = (0..s.chunks.len()).map(|i| chunk_bytes(s.label.as_bytes(), i)).collect(); diff --git a/wai/wai-rs/src/live_ads/corpus.rs b/wai/wai-rs/src/live_ads/corpus.rs index dd786e417..8369b6ea1 100644 --- a/wai/wai-rs/src/live_ads/corpus.rs +++ b/wai/wai-rs/src/live_ads/corpus.rs @@ -409,7 +409,7 @@ struct LiveBreak { fn live_break(spec: Spec) -> LiveBreak { let mut room = room(spec); - room.raw(&key(PRODUCER), 10 * S, kind::CHAT, Payload::Chat { text: "kick-off".into() }.encode()); + room.raw(&key(PRODUCER), 10 * S, kind::CHAT, Payload::Chat { text: "kick-off".into(), origin: None }.encode()); let out = room.cue(PRODUCER, 60 * S, cue_type::OUT, 101, 0); let in_ = room.cue(PRODUCER, 90 * S, cue_type::IN, 101, 30_000); let policy = policy(&room, |_| {}); diff --git a/wai/wai-rs/src/live_compose/corpus.rs b/wai/wai-rs/src/live_compose/corpus.rs index 64760a0aa..09a1b3f97 100644 --- a/wai/wai-rs/src/live_compose/corpus.rs +++ b/wai/wai-rs/src/live_compose/corpus.rs @@ -826,10 +826,10 @@ fn studio(v: Variant) -> Written { c.life(S, DIRECTOR, own(media::VIDEO, 2, 2)); c.life(2 * S, MIXER, own(media::VIDEO, 3, 3)); c.life(2 * S, MIXER, own(media::AUDIO, 1, 1)); - c.emit(VIEWER, 2_500 * MS, &Payload::Chat { text: "hello".into() }); + c.emit(VIEWER, 2_500 * MS, &Payload::Chat { text: "hello".into(), origin: None }); c.life(3 * S, GUEST, Detail::Join { grant: gg.digest() }); c.life(3 * S, GUEST, own(media::AUDIO, 0, 1)); - c.emit(VIEWER, 3_500 * MS, &Payload::Chat { text: "welcome".into() }); + c.emit(VIEWER, 3_500 * MS, &Payload::Chat { text: "welcome".into(), origin: None }); c.life(4 * S, SCREEN, Detail::Join { grant: gs.digest() }); c.life(4 * S, SCREEN, own(media::SCREEN, 4, 1)); c.life(4 * S, ENCODER, Detail::Join { grant: ge.digest() }); diff --git a/wai/wai-rs/src/live_events.rs b/wai/wai-rs/src/live_events.rs index 1f7ac92a6..60c1c0a1e 100644 --- a/wai/wai-rs/src/live_events.rs +++ b/wai/wai-rs/src/live_events.rs @@ -446,6 +446,30 @@ pub mod scte_form { pub const EVENT_BASED: u8 = 2; } +/// The origin a `chat` or a `reaction` may state (§4.4, §4.5), as a `caption` states its +/// own (§4.10). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Origin { + /// 1 human, 2 machine, 3 machine-reviewed. + pub origin: u8, + /// The digest naming the model, present exactly when `origin` is 2 or 3. + pub model: Option<[u8; 32]>, +} + +impl Origin { + /// Whether it states that a machine produced the content: origin 2 or 3. + pub fn is_machine(&self) -> bool { + matches!(self.origin, 2 | 3) + } + + fn encode(&self, o: &mut Vec) { + o.push(self.origin); + if let Some(m) = self.model { + o.extend_from_slice(&m); + } + } +} + /// A `reaction` (§4.5). #[derive(Clone, Debug, PartialEq, Eq)] pub struct Reaction { @@ -453,6 +477,8 @@ pub struct Reaction { pub value: u16, pub target: Option<[u8; 32]>, pub remove: bool, + /// The origin it states, if it states one. + pub origin: Option, } /// A `marker` (§4.8). @@ -492,7 +518,8 @@ pub struct Caption { pub enum Payload { Meta(Vec), Cue(Cue), - Chat { text: String }, + /// A message, and the origin it states, if it states one (§4.4). + Chat { text: String, origin: Option }, Reaction(Reaction), PollOpen { question: String, options: Vec }, Vote { poll: [u8; 32], option: u8 }, @@ -549,7 +576,12 @@ impl Payload { } } } - Payload::Chat { text } => put_text(&mut o, text), + Payload::Chat { text, origin } => { + put_text(&mut o, text); + if let Some(g) = origin { + g.encode(&mut o); + } + } Payload::Reaction(r) => { o.push(r.rtype); o.extend_from_slice(&r.value.to_le_bytes()); @@ -557,6 +589,9 @@ impl Payload { if let Some(t) = r.target { o.extend_from_slice(&t); } + if let Some(g) = &r.origin { + g.encode(&mut o); + } } Payload::PollOpen { question, options } => { put_text(&mut o, question); @@ -670,6 +705,22 @@ pub fn splice_info_section_ok(b: &[u8]) -> bool { b.len() >= 3 && b[0] == 0xFC && 3 + ((((b[1] & 0x0F) as usize) << 8) | b[2] as usize) == b.len() } +/// §4.4, §4.5: the optional origin after a `chat`'s or a `reaction`'s last field — `origin` +/// (1, 2 or 3), then `model` for 2 and 3 — or none when no byte follows. +fn read_origin(r: &mut Rd) -> Result, Reason> { + if r.done() { + return Ok(None); + } + let m = Reason::MalformedPayload; + let origin = r.u8().ok_or(m)?; + let model = match origin { + 1 => None, + 2 | 3 => Some(r.d32().ok_or(m)?), + _ => return Err(m), + }; + Ok(Some(Origin { origin, model })) +} + /// Read a payload of kind `k` (§4), in the order its fields are written; each field's own /// rule applies when it is read, and bytes after the last field are `malformed_payload`. /// `k` is a kind this revision defines. @@ -701,7 +752,10 @@ pub fn parse_payload(k: u8, b: &[u8], max_text_cp: u16) -> Result Payload::Chat { text: read_text(&mut r, max_text_cp)? }, + kind::CHAT => { + let text = read_text(&mut r, max_text_cp)?; + Payload::Chat { text, origin: read_origin(&mut r)? } + } kind::REACTION => { let rtype = r.u8().ok_or(m)?; if !(1..=5).contains(&rtype) { @@ -716,7 +770,7 @@ pub fn parse_payload(k: u8, b: &[u8], max_text_cp: u16) -> Result { let question = read_text(&mut r, max_text_cp)?; @@ -1692,7 +1746,7 @@ mod tests { } fn chat(s: &str) -> Payload { - Payload::Chat { text: s.into() } + Payload::Chat { text: s.into(), origin: None } } #[test] @@ -1746,7 +1800,11 @@ mod tests { Payload::Cue(Cue { cue_type: cue_type::BREAK, cue_id: 8, duration_ms: 0, scte: Some((2, vec![1])) }), Payload::Cue(Cue { cue_type: cue_type::CHAPTER, cue_id: 9, duration_ms: 1, scte: None }), chat("héllo"), - Payload::Reaction(Reaction { rtype: 1, value: 3, target: Some(d), remove: true }), + Payload::Chat { text: "hi".into(), origin: Some(Origin { origin: 1, model: None }) }, + Payload::Chat { text: "hi".into(), origin: Some(Origin { origin: 2, model: Some(d) }) }, + Payload::Reaction(Reaction { rtype: 1, value: 3, target: Some(d), remove: true, origin: None }), + Payload::Reaction(Reaction { rtype: 1, value: 3, target: Some(d), remove: false, origin: Some(Origin { origin: 3, model: Some(d) }) }), + Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false, origin: Some(Origin { origin: 2, model: Some(d) }) }), Payload::PollOpen { question: "q".into(), options: vec!["a".into(), "b".into()] }, Payload::Vote { poll: d, option: 1 }, Payload::PollClose { poll: d, counts: vec![1, 2] }, @@ -1772,6 +1830,22 @@ mod tests { } } + /// §4.4, §4.5: a chat or a reaction states an origin of 1, 2 or 3, with a model exactly + /// for 2 and 3; anything else after its last field is `malformed_payload`. + #[test] + fn a_stated_origin_reads_as_a_captions_does() { + let text = chat("x").encode(); + for (tail, ok) in [(vec![1], true), (vec![2], false), (vec![1, 0], false), (vec![0], false), (vec![4], false)] { + let b = [text.clone(), tail.clone()].concat(); + assert_eq!(parse_payload(kind::CHAT, &b, 500).is_ok(), ok, "{tail:?}"); + } + let b = [text.clone(), vec![3], vec![5; 32]].concat(); + assert_eq!(parse_payload(kind::CHAT, &b, 500), Ok(Payload::Chat { text: "x".into(), origin: Some(Origin { origin: 3, model: Some([5; 32]) }) })); + let r = Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false, origin: None }).encode(); + assert_eq!(parse_payload(kind::REACTION, &[r.clone(), vec![2]].concat(), 500), Err(Reason::MalformedPayload)); + assert!(parse_payload(kind::REACTION, &[r, vec![2], vec![6; 32]].concat(), 500).is_ok()); + } + #[test] fn text_rules() { let mut b = vec![2, 0, 0xC0, 0xAF]; @@ -1840,7 +1914,7 @@ mod tests { let mut a = Author::new(4); let rs: Vec> = [0, 500_000, 1_000_000].iter().map(|&t| a.emit(t, chat("x")).to_bytes()).collect(); assert_eq!(verify_chain(ROOM, p, &rs), Outcome::Accept); - let r = |target| Payload::Reaction(Reaction { rtype: 1, value: 1, target, remove: false }); + let r = |target| Payload::Reaction(Reaction { rtype: 1, value: 1, target, remove: false, origin: None }); let mut a = Author::new(4); let rs = vec![a.emit(0, r(None)).to_bytes(), a.emit(10, r(Some([1; 32]))).to_bytes(), a.emit(999_999, r(None)).to_bytes()]; assert_eq!(verify_chain(ROOM, Policy::default(), &rs), Outcome::Refuse { index: 2, reason: Reason::RateExceeded }); diff --git a/wai/wai-rs/src/live_events/corpus.rs b/wai/wai-rs/src/live_events/corpus.rs index f1c964351..e246d4d8b 100644 --- a/wai/wai-rs/src/live_events/corpus.rs +++ b/wai/wai-rs/src/live_events/corpus.rs @@ -95,7 +95,7 @@ fn video(group: u64, object: u64) -> Anchor { } fn chat(s: &str) -> Payload { - Payload::Chat { text: s.into() } + Payload::Chat { text: s.into(), origin: None } } fn digest_of(record: &[u8]) -> [u8; 32] { @@ -165,7 +165,7 @@ fn caption(duration_ms: u32, origin: u8, lang: &str, text: &str) -> Payload { } fn reaction(rtype: u8, value: u16, target: Option<[u8; 32]>, remove: bool) -> Payload { - Payload::Reaction(Reaction { rtype, value, target, remove }) + Payload::Reaction(Reaction { rtype, value, target, remove, origin: None }) } fn lifecycle(kind: u8, subject: u8, detail: &[u8]) -> Payload { @@ -333,6 +333,22 @@ fn good_vectors() -> Vec { Intent::Accept, )); + let mut a = Author::new(ALICE); + let model = sha(&[b"wai:live-events/corpus/model/", b"chat"]); + let said = a.at(S, Payload::Chat { text: "Here is a summary of the talk so far.".into(), origin: Some(Origin { origin: 2, model: Some(model) }) }); + let target = digest_of(&said); + out.push(v( + "kind_chat_origin", + "chat and reaction stating their origin (§4.4, §4.5): a machine message with its model's digest, a machine-reviewed one, a human one stating origin 1, and a machine reaction targeting the first", + vec![ + said, + a.at(2 * S, Payload::Chat { text: "Reviewed: the summary above is accurate.".into(), origin: Some(Origin { origin: 3, model: Some(model) }) }), + a.at(3 * S, Payload::Chat { text: "Typed by a person.".into(), origin: Some(Origin { origin: 1, model: None }) }), + a.at(4 * S, Payload::Reaction(Reaction { rtype: 1, value: 1, target: Some(target), remove: false, origin: Some(Origin { origin: 2, model: Some(model) }) })), + ], + Intent::Accept, + )); + let mut h = Author::new(HOST); out.push(v( "kind_caption", @@ -653,6 +669,15 @@ fn negative_vectors() -> Vec { )); let mut h = Author::new(HOST); out.push(v("malformed_payload_lang", "a caption whose language tag is one letter (§4.10)", vec![h.at(S, caption(1000, 1, "e", "x"))], refuse(0, Reason::MalformedPayload))); + let mut a = Author::new(ALICE); + let mut bytes = chat("which model?").encode(); + bytes.push(2); + out.push(v( + "malformed_payload_origin", + "a chat stating origin 2 with no model digest after it (§4.4)", + vec![a.raw(S, None, kind::CHAT, bytes).to_bytes()], + refuse(0, Reason::MalformedPayload), + )); let mut h = Author::new(HOST); out.push(v("malformed_payload_poll", "a poll_open with one option (§4.6)", vec![h.at(S, poll_open("Only one?", &["Yes"]))], refuse(0, Reason::MalformedPayload))); diff --git a/wai/wai-rs/src/live_metering/corpus.rs b/wai/wai-rs/src/live_metering/corpus.rs index 967a47471..d5483cd4a 100644 --- a/wai/wai-rs/src/live_metering/corpus.rs +++ b/wai/wai-rs/src/live_metering/corpus.rs @@ -376,8 +376,8 @@ fn lesson(s: Scene) -> WrittenRoom { for p in &plans { c.emit(DIRECTOR, STOP_T, &Payload::Control { target: pk(p.device), command: Command::RecordStop }); } - c.emit(VIEWER, START_T + 500 * MS, &Payload::Chat { text: "good luck".into() }); - c.emit(VIEWER2, 18 * S, &Payload::Chat { text: "bravo".into() }); + c.emit(VIEWER, START_T + 500 * MS, &Payload::Chat { text: "good luck".into(), origin: None }); + c.emit(VIEWER2, 18 * S, &Payload::Chat { text: "bravo".into(), origin: None }); // Each take: its commitments in the device's chain, and its manifest. let mut takes = Vec::new(); for (k, p) in plans.iter().enumerate() { diff --git a/wai/wai-rs/src/live_moderation/corpus.rs b/wai/wai-rs/src/live_moderation/corpus.rs index 636a79687..ab3ec3859 100644 --- a/wai/wai-rs/src/live_moderation/corpus.rs +++ b/wai/wai-rs/src/live_moderation/corpus.rs @@ -179,11 +179,11 @@ fn role(name: &str, powers: u8, events: &[u8]) -> Role { } fn chat(s: &str) -> Payload { - Payload::Chat { text: s.into() } + Payload::Chat { text: s.into(), origin: None } } fn emoji(target: Option<[u8; 32]>) -> Payload { - Payload::Reaction(Reaction { rtype: 1, value: 1, target, remove: false }) + Payload::Reaction(Reaction { rtype: 1, value: 1, target, remove: false, origin: None }) } /// The chat room: a host and a moderator with the moderate power, two viewers, and the diff --git a/wai/wai-rs/src/live_record/corpus.rs b/wai/wai-rs/src/live_record/corpus.rs index b9ec45733..61841e4bb 100644 --- a/wai/wai-rs/src/live_record/corpus.rs +++ b/wai/wai-rs/src/live_record/corpus.rs @@ -344,7 +344,7 @@ fn write(s: &Scene) -> Written { for t in &s.takes { rm.emit(DIRECTOR, s.stop_t, &Payload::Control { target: pk(t.device), command: Command::RecordStop }); } - rm.emit(VIEWER, s.start_t + 500 * MS, &Payload::Chat { text: "good luck".into() }); + rm.emit(VIEWER, s.start_t + 500 * MS, &Payload::Chat { text: "good luck".into(), origin: None }); // Each take. let mut files = BTreeMap::new(); let mut written = Vec::new(); diff --git a/wai/wai-rs/src/live_room.rs b/wai/wai-rs/src/live_room.rs index 561b5e630..3d763da11 100644 --- a/wai/wai-rs/src/live_room.rs +++ b/wai/wai-rs/src/live_room.rs @@ -148,6 +148,7 @@ pub enum Reason { ParticipantLimit, OutsideGrantWindow, PowerNotGranted, + AiUnmarked, PublishNotPermitted, ProfileNotGranted, AlreadyPublished, @@ -179,7 +180,7 @@ impl From for Reason { impl Reason { /// The codes this extension adds, in §11's order. - pub const ALL: [Reason; 42] = [ + pub const ALL: [Reason; 43] = [ Reason::DescriptorInvalid, Reason::KeyLogMissing, Reason::KeyLogNotHeld, @@ -201,6 +202,7 @@ impl Reason { Reason::ParticipantLimit, Reason::OutsideGrantWindow, Reason::PowerNotGranted, + Reason::AiUnmarked, Reason::PublishNotPermitted, Reason::ProfileNotGranted, Reason::AlreadyPublished, @@ -249,6 +251,7 @@ impl Reason { Reason::ParticipantLimit => "participant_limit", Reason::OutsideGrantWindow => "outside_grant_window", Reason::PowerNotGranted => "power_not_granted", + Reason::AiUnmarked => "ai_unmarked", Reason::PublishNotPermitted => "publish_not_permitted", Reason::ProfileNotGranted => "profile_not_granted", Reason::AlreadyPublished => "already_published", @@ -1203,8 +1206,11 @@ impl Grant { if raw.origins.len() > 8 || !raw.origins.iter().all(|o| token_ok(o, 255)) { return bad; } - if read_attrs(&raw.attrs).is_none() { - return bad; + match read_attrs(&raw.attrs) { + None => return bad, + // §4.4: `ai` is a model's digest, as 64 lowercase hexadecimal digits. + Some(a) if a.iter().any(|(k, v)| k == AI_ATTR && hex32(v).is_none()) => return bad, + Some(_) => {} } Ok(()) } @@ -1237,6 +1243,12 @@ impl Grant { Ok(()) } + /// The model an AI participant's grant names in its `ai` attribute (§4.4), or `None` for + /// a grant without one. + pub fn ai_model(&self) -> Option<[u8; 32]> { + self.attrs.iter().find(|(k, _)| k == AI_ATTR).and_then(|(_, v)| hex32(v)) + } + /// The revocation tag of the grant's viewer (§5), when it names one. pub fn viewer_tag(&self) -> Option<[u8; 32]> { (self.viewer != [0; 16]).then(|| sha256(&[VIEWER_DOMAIN, &self.viewer])) @@ -1268,6 +1280,36 @@ fn read_grant(b: &[u8], d: &Descriptor) -> Result<(RawGrant, [u8; 32]), Reason> Ok((raw, sha256(&[body]))) } +/// The attribute that marks an AI participant (§4.4). +pub const AI_ATTR: &str = "ai"; + +/// 64 lowercase hexadecimal digits, read as 32 bytes. +fn hex32(s: &str) -> Option<[u8; 32]> { + let b = s.as_bytes(); + if b.len() != 64 || !b.iter().all(|c| c.is_ascii_digit() || (b'a'..=b'f').contains(c)) { + return None; + } + let mut o = [0u8; 32]; + for (i, o) in o.iter_mut().enumerate() { + *o = u8::from_str_radix(&s[2 * i..2 * i + 2], 16).ok()?; + } + Some(o) +} + +/// §9.2 step 6: an AI participant's `chat`, `reaction` and `caption` state that a machine +/// produced them, with the model its grant names. Any other payload, or a principal whose +/// grant names no model, is not held to it. +pub fn ai_marked(g: &Grant, p: &Payload) -> bool { + let Some(model) = g.ai_model() else { return true }; + let stated = match p { + Payload::Chat { origin, .. } => origin.map(|o| (o.origin, o.model)), + Payload::Reaction(r) => r.origin.map(|o| (o.origin, o.model)), + Payload::Caption(c) => Some((c.origin, c.model)), + _ => return true, + }; + matches!(stated, Some((2 | 3, Some(m))) if m == model) +} + /// A grant a room verifier holds, with its digest. #[derive(Clone, Debug)] pub struct HeldGrant { @@ -1988,6 +2030,12 @@ fn step(replay: &mut Replay, verifier: &mut RoomVerifier, seen: &mut Seen, b: &[ return Err(Reason::PowerNotGranted); } } + // f. An AI participant's chat, reaction and caption state their machine origin (§4.4). + if let Some(a) = verifier.accepted().last() { + if !ai_marked(&h.grant, &a.payload) { + return Err(Reason::AiUnmarked); + } + } Ok(Some(h.digest)) } diff --git a/wai/wai-rs/src/live_room/corpus.rs b/wai/wai-rs/src/live_room/corpus.rs index 018b2d3c5..305392970 100644 --- a/wai/wai-rs/src/live_room/corpus.rs +++ b/wai/wai-rs/src/live_room/corpus.rs @@ -301,7 +301,7 @@ impl Build { } fn chat(s: &str) -> Payload { - Payload::Chat { text: s.into() } + Payload::Chat { text: s.into(), origin: None } } fn control(target: u8, command: Command) -> Payload { @@ -372,7 +372,7 @@ fn lesson() -> (Build, [u8; 32]) { b.ev(PERFORMER, 8 * S, take_commit(1)); b.ev(DIRECTOR, 9 * S, Payload::Marker(Marker { taxonomy: 2, polarity: 2, text: "rushed bar 12".into(), attachment: None })); b.ev(DIRECTOR, 10 * S, control(PERFORMER, Command::RecordStop)); - b.ev(VIEWER_B, 10_500_000, Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false })); + b.ev(VIEWER_B, 10_500_000, Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false, origin: None })); b.ev(DIRECTOR, 11 * S, chat("again from bar 9")); b.life(12 * S, VIEWER_B, Detail::Leave { reason: leave::LEFT }); (b, revoked) @@ -408,7 +408,7 @@ fn broadcast() -> Build { b.ev(VIEWER_C, 4_200_000, Payload::Vote { poll, option: 0 }); let spam = b.ev(VIEWER_C, 5 * S, chat("first!!! follow the link in my name")); b.ev(MODERATOR, 5_500_000, Payload::Decision { verdict: 2, target: spam, reason: 3 }); - b.ev(VIEWER_A, 6 * S, Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false })); + b.ev(VIEWER_A, 6 * S, Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false, origin: None })); b.ev(HOST, 7 * S, Payload::PollClose { poll, counts: vec![1, 2] }); b.life(8 * S, VIEWER_C, Detail::Leave { reason: leave::REMOVED }); b @@ -483,7 +483,7 @@ fn dvr() -> Build { b.join(S, VIEWER_A, da); b.join(2 * S, VIEWER_B, db); b.ev(VIEWER_A, 3 * S, chat("going back to the opening")); - b.ev(VIEWER_B, 4 * S, Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false })); + b.ev(VIEWER_B, 4 * S, Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false, origin: None })); b } @@ -527,7 +527,7 @@ fn good_vectors() -> Vec { b.publish(11 * S, VIEWER_A, "", media::AUDIO, 0, 1); b.publish(11 * S, VIEWER_A, "", media::VIDEO, 1, 2); b.ev(VIEWER_A, 12 * S, Payload::Marker(Marker { taxonomy: 1, polarity: 1, text: "question".into(), attachment: None })); - b.ev(VIEWER_B, 12 * S, Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false })); + b.ev(VIEWER_B, 12 * S, Payload::Reaction(Reaction { rtype: 4, value: 1, target: None, remove: false, origin: None })); out.push(vector( "promote_viewer", "a viewer's audience grant exchanged at the 10 s tick for a guest grant chained to it: before, it may chat only; after, it publishes audio and video and sends a marker (§4.6)", @@ -711,6 +711,12 @@ fn grant_vectors() -> Vec { &one(&|b| Grant { policy: Policy::with_kinds(&[kind::CHAT, kind::JOIN]), ..b.grant(VIEWER_A, 2) }.sign(&key(AUTHORITY)).to_bytes()), refuse(1, Reason::GrantInvalid), )); + out.push(vector( + "grant_ai_malformed", + "a grant whose ai attribute is not 64 lowercase hexadecimal digits (§4.4)", + &one(&|b| Grant { attrs: vec![("ai".into(), "A".repeat(64))], ..b.grant(VIEWER_A, 2) }.sign(&key(AUTHORITY)).to_bytes()), + refuse(1, Reason::GrantInvalid), + )); out.push(vector( "grant_attrs_too_long", "a grant whose attributes take more than 1024 bytes (§4.4)", @@ -1008,6 +1014,38 @@ fn record_vectors() -> Vec { let r = refuse_last(&b, HOST, Reason::PowerNotGranted); out.push(vector("power_not_granted", "a host with the direct power but not record mutes a viewer, then sends record_start (§3.3)", &b, r)); + // An AI participant (§4.4): a host whose grant names the model that acts for it. + let model = sha(&[b"wai:live-rooms/corpus/model"]); + let ai_room = |says: Option| { + let mut b = Build::new(base()); + let g = Grant { attrs: vec![("ai".into(), hex(&model))], ..b.grant(HOST, 0) }; + let gv = b.grant(VIEWER_A, 2); + let (d, dv) = (b.add(g), b.add(gv)); + b.join(0, HOST, d); + b.join(0, VIEWER_A, dv); + b.ev(VIEWER_A, S, Payload::Chat { text: "Is anyone there?".into(), origin: None }); + b.ev(HOST, 2 * S, Payload::Chat { text: "Welcome. I am an automated host.".into(), origin: says }); + b + }; + let mut b = ai_room(Some(events::Origin { origin: 2, model: Some(model) })); + b.ev(HOST, 3 * S, Payload::Caption(events::Caption { duration_ms: 2_000, origin: 3, model: Some(model), lang: "en".into(), text: "Welcome.".into() })); + b.ev(VIEWER_A, 4 * S, Payload::Reaction(events::Reaction { rtype: 4, value: 1, target: None, remove: false, origin: None })); + out.push(vector( + "ai_participant", + "a host whose grant's ai attribute names a model: its chat states origin 2 and its caption origin 3, each with that model; a person's chat and reaction state none (§4.4, §9.2 step 6)", + &b, + Intent::Accept, + )); + let b = ai_room(None); + let r = refuse_last(&b, HOST, Reason::AiUnmarked); + out.push(vector("ai_unmarked", "an AI participant's chat that states no origin (§9.2 step 6)", &b, r)); + let b = ai_room(Some(events::Origin { origin: 1, model: None })); + let r = refuse_last(&b, HOST, Reason::AiUnmarked); + out.push(vector("ai_unmarked_human", "an AI participant's chat that states origin 1, human (§9.2 step 6)", &b, r)); + let b = ai_room(Some(events::Origin { origin: 2, model: Some(sha(&[b"wai:live-rooms/corpus/other-model"])) })); + let r = refuse_last(&b, HOST, Reason::AiUnmarked); + out.push(vector("ai_unmarked_model", "an AI participant's chat that states origin 2 with a model other than its grant's (§9.2 step 6)", &b, r)); + let mut b = Build::new(base()); let g = b.grant(VIEWER_A, 2); let g = Grant { policy: Policy { max_events: 2, ..g.policy }, ..g }; diff --git a/wai/wai-rs/tests/live_room.rs b/wai/wai-rs/tests/live_room.rs index 00da60711..213ced596 100644 --- a/wai/wai-rs/tests/live_room.rs +++ b/wai/wai-rs/tests/live_room.rs @@ -39,7 +39,7 @@ fn the_corpus_covers_every_reason_and_room() { for r in Reason::ALL { assert!(reasons.contains(r.code()), "nothing in the corpus is refused with {r}"); } - for name in ["lesson", "broadcast", "takeover", "redundant_failover", "promote_viewer", "dvr"] { + for name in ["lesson", "broadcast", "takeover", "redundant_failover", "promote_viewer", "dvr", "ai_participant"] { let v = vectors.iter().find(|v| v["name"] == name).unwrap_or_else(|| panic!("no vector {name}")); assert_eq!(v["outcome"], "accept", "{name}"); } -- 2.54.0 (Apple Git-157) From 03117478c41be6ce59a160616e3ca7a41e76dbcc Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 05:17:43 -0400 Subject: [PATCH 2/9] wai.media.synthesis: origin claims for every kind of media, two marking layers, reviews and labels 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 --- wai/extensions/synthesis.md | 760 +++++++++ wai/live-conformance/README.md | 9 +- wai/live-conformance/synthesis/README.md | 38 + wai/live-conformance/synthesis/vectors.json | 1552 +++++++++++++++++++ wai/wai-rs/Cargo.toml | 19 + wai/wai-rs/src/bin/wai_synthesis_vectors.rs | 74 + wai/wai-rs/src/lib.rs | 9 + wai/wai-rs/src/synthesis.rs | 1402 +++++++++++++++++ wai/wai-rs/src/synthesis/corpus.rs | 1088 +++++++++++++ wai/wai-rs/tests/synthesis.rs | 71 + 10 files changed, 5019 insertions(+), 3 deletions(-) create mode 100644 wai/extensions/synthesis.md create mode 100644 wai/live-conformance/synthesis/README.md create mode 100644 wai/live-conformance/synthesis/vectors.json create mode 100644 wai/wai-rs/src/bin/wai_synthesis_vectors.rs create mode 100644 wai/wai-rs/src/synthesis.rs create mode 100644 wai/wai-rs/src/synthesis/corpus.rs create mode 100644 wai/wai-rs/tests/synthesis.rs diff --git a/wai/extensions/synthesis.md b/wai/extensions/synthesis.md new file mode 100644 index 000000000..d8eb9792c --- /dev/null +++ b/wai/extensions/synthesis.md @@ -0,0 +1,760 @@ +# 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 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, 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 the +> `synthesis` module of `wai-rs` (feature `synthesis`); the corpus is +> [`live-conformance/synthesis/`](../live-conformance/synthesis/), and its second +> implementation is `live-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](provenance.md)'s; key discovery for the signers of these records, which a verifier +takes from [jwp-receipts](jwp-receipts.md) §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](../SPEC.md) §5): a +signed record a WAI tool emits and verifies. It names no payload format and is not dispatchable. + +Relationship to [timed-text](timed-text.md) and [live-events](live-events.md): 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](energy-binding.md) §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](determinism-tiers.md): 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](jwp-receipts.md) §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](../SPEC.md) §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](reconstruction-binding.md) §2 (row-major, three bytes a +pixel, no row padding); for another medium, the canonical buffer its capability's +`*-equivalence` names ([determinism-tiers](determinism-tiers.md) §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](derived-tracks.md)). 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 `origin` names a claim the verifier holds: that claim's + subject is the object of the input's `content_hash`, and its `class` and `source` are the + input's. Otherwise `input_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 `GenerativeDeterministic` + or `Behavioral` (SPEC §7), or whose manifest's `intent` is `create`, is generated by every sink + that presents it, and its claim's `step` is `generate`. A claim of another step over such an + envelope is `class_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 2*w* × 2*h* 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(2*x*, 2*y*) = *a*; +- out(2*x* + 1, 2*y*) = (*a* + *b* + 1) >> 1; +- out(2*x*, 2*y* + 1) = (*a* + *c* + 1) >> 1; +- out(2*x* + 1, 2*y* + 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: + +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**. Without the input's samples it + is **not recomputed**; `attest` evidence is **asserted**. +3. With the subject's decoded samples held (an object subject): their digest 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: + +| 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}]`, + `U` the IPTC term of the claim's `source`; + - a `convert` of one input: `[{"action": "c2pa.opened"}, {"action": "c2pa.converted"}]`, with + no digital source type, as [energy-binding](energy-binding.md) §6.1 has it for a decode: the + input is the parent ingredient, and its own manifest states its source; + - an `assist` of one input: `[{"action": "c2pa.opened"}, {"action": "c2pa.edited", + "digitalSourceType": U}]`, `U` the term of `algorithmicallyEnhanced`; + - a `generate` of one input: the same, `U` the term of the generated source (§5). +- `D`, one `c2pa.ai-disclosure` assertion (C2PA 2.4 §18.28) for each model whose `type` is + `trained`, in the claim's order, labelled `c2pa.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 a + `generate`, `"contentProfile": {"humanOversightLevel": O}` in `data`, `O` the claim's + `oversight`. A model of type `algorithmic` has 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](reconstruction-binding.md) 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): + +1. With a marking: the detection's `scheme` and `scheme_digest` are the marking's + (`detection_mismatch`). +2. 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**. +3. 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:` 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](live-rooms.md) §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](live-events.md) §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](determinism-tiers.md) §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](determinism-tiers.md) §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: + +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: + +| 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 `cue` event, 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 from `at` on: its content hash; the link hash of the + origin claim it applied, or `null`; that claim's class as it read it, or `null`; the `media` of + §3.1; the tier its path holds (`decode-equivalence`, `generation-equivalence`, + `entropy-consistency` or `none`, §9.2); its decoder (a term) and variant (a term or `null`), as a + `reconstruct-session` claim names them (jwp-receipts §2.4.12); and its output's digest, or `null` + where it states none. +- `{"type": "shown", "label", "asset", "trigger"}`: the player showed a label: `visual` or `audio`; + the SHA-256 of the asset shown; and why, as the player states it: `first-exposure`, `resume` or + `interval`. +- `{"type": "resume", "after"}`: presentation resumed after an interruption, `ad-break` or + `other`. +- `{"type": "review", "subject", "review"}`: the player presented the review whose link hash is + `review` for the object `subject` (§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: + +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; 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. + +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`); the detections + in order (§7.3: `detection_mismatch`, `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 + +- 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-recomputed` or `asserted` (§4.4); +- for each detection, in order: its object and its result, `agree`, `unmarked` or `not-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 `review` entry 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`, + `resume` or `interval`) and the index of the first `shown` entry that met it; +- the file name of the EU icon the label policy's `visual` is, where it is one (§10.2); +- the room's own outcome, `accept` or `gap`, 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/`](../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.spectral` envelope, 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). diff --git a/wai/live-conformance/README.md b/wai/live-conformance/README.md index eff884d3d..9bf83749e 100644 --- a/wai/live-conformance/README.md +++ b/wai/live-conformance/README.md @@ -17,6 +17,7 @@ and the independent verifier of each, beside it. | [`ads/`](ads/) | [live-ads](../extensions/live-ads.md) (`wai.live.ads`) | `live_ads_verify.py` | | [`e2ee/`](e2ee/) | [confidentiality](../extensions/confidentiality.md) (`wai.live.e2ee`) | `e2ee_verify.py` | | [`watermark/`](watermark/) | [forensic-watermark](../extensions/forensic-watermark.md) (`wai.live.watermark`) | `live_watermark_verify.py` | +| [`synthesis/`](synthesis/) | [synthesis](../extensions/synthesis.md) (`wai.media.synthesis`) | `synthesis_verify.py` | **Provenance.** Generated here: `wai_live_events_vectors gen` writes `events/`, `wai_live_room_vectors gen` writes `rooms/`, `wai_capture_take_vectors gen` writes @@ -24,11 +25,12 @@ and the independent verifier of each, beside it. writes `recordings/`, `wai_timed_text_vectors gen` writes `captions/` and `wai_live_metering_vectors gen` writes `metering/`, `wai_energy_steering_vectors gen` writes `steering/`, `wai_live_moderation_vectors gen` writes `moderation/`, -`wai_live_watermark_vectors gen` writes `watermark/` and `wai_live_ads_vectors gen` writes -`ads/`, each from records authored in code, from fixed seeds (`events/README.md`, +`wai_live_watermark_vectors gen` writes `watermark/`, `wai_live_ads_vectors gen` writes +`ads/` and `wai_synthesis_vectors gen` writes `synthesis/`, each from records authored in code, +from fixed seeds (`events/README.md`, `rooms/README.md`, `takes/README.md`, `compose/README.md`, `recordings/README.md`, `captions/README.md`, `metering/README.md`, `steering/README.md`, `moderation/README.md`, -`watermark/README.md`, `ads/README.md`). `joule_moq_e2ee_vectors gen` (in +`watermark/README.md`, `ads/README.md`, `synthesis/README.md`). `joule_moq_e2ee_vectors gen` (in `joule-code/joule-code-rs/crates/joule-moq-mls`) writes `e2ee/vectors.json` from fixed seeds, and `e2ee/` also holds the test vectors RFC 9605 and `draft-ietf-moq-secure-objects-01` publish, as printed (`e2ee/README.md`). One recorded record: `metering/relay-record.wmh` and @@ -51,4 +53,5 @@ python3 live_moderation_verify.py moderation python3 live_ads_verify.py ads python3 e2ee_verify.py e2ee python3 live_watermark_verify.py watermark +python3 synthesis_verify.py synthesis ``` diff --git a/wai/live-conformance/synthesis/README.md b/wai/live-conformance/synthesis/README.md new file mode 100644 index 000000000..68e61abbf --- /dev/null +++ b/wai/live-conformance/synthesis/README.md @@ -0,0 +1,38 @@ +# live-conformance/synthesis — synthesis provenance + +The corpus of [`extensions/synthesis.md`](../../extensions/synthesis.md) §13, in one file, +`vectors.json`: the EU icons §10.2 pins (`eu_icons`, each its `file` and `sha256`), and the +**vectors**, each a case of §11.1 and what a verifier gives it. + +A case holds its `policy` (`dual_layer`, `assistive`); its records as JSON texts, list by list: +`claims` (origin claims), `markings`, `detections`, `reviews`, `label_policy` (a text or +`null`) and `player` (the log's entries); the `objects` it holds, each a `WAI1` envelope in +hexadecimal; the `decoded` samples it holds, each the content hash of the `object` decoded and +its canonical `samples` in hexadecimal; and a `room` or `null`: its `descriptor`, `grants`, +`key_log` and `chains` as live-rooms' corpus gives them. + +**Synthesis equivalence.** For each vector, a conforming verifier reaches the outcome it states: + +- `refuse`: the `reason` and where, `at`: `["claim", i]`, `["marking", i]`, `["detection", i]`, + `["review", i]`, `["label_policy"]`, `["player", i]`, or `["room", …]` with the room verifier's + own place (`["room", "record", c, i]` for record `i` of chain `c`); +- `accept`: the `statements` of §11.3: `claims`, each its `link`, `class`, `source`, `c2pa` (the + statement of §6, exactly as written) and `assistive` (`proved`, `not-recomputed`, `asserted`, or + `null` for a claim that is not an assist); `detections`, each its `subject` and `result`; + `ai_tracks`, each its `track` and `claim`; `reviews_presented`, each its log `entry` and + `review`; `labels`, each its log `entry`, `trigger` and the `shown` entry that met it; + `visual_icon`, the file of §10.2 the label policy's visual is, or `null`; and `room`, the room's + own outcome or `null`. + +A verifier that reaches another outcome, reason, place or statement does not conform. + +**Provenance.** `wai_synthesis_vectors gen` writes `vectors.json` from records authored in code: +every key is SHA-256 of the corpus's label and an index, every sample buffer and envelope payload +is SHA-256 output of a label, every time is a constant, and Ed25519 signing is deterministic, so +the file is the same bytes on every machine. The watermark scheme the markings and detections +name, `wai-mark:conformance`, names no algorithm (§7.4): the corpus exercises the records and the +agreement of the two layers, not a watermark. + +``` +python3 -I ../synthesis_verify.py . +``` diff --git a/wai/live-conformance/synthesis/vectors.json b/wai/live-conformance/synthesis/vectors.json new file mode 100644 index 000000000..5dd70b7b8 --- /dev/null +++ b/wai/live-conformance/synthesis/vectors.json @@ -0,0 +1,1552 @@ +{ + "about": "the conformance corpus of extensions/synthesis.md §13, written by wai_synthesis_vectors gen", + "eu_icons": [ + { + "file": "LABEL_AI GENERATED_black transparent.svg", + "sha256": "63d28ab55916b4548edff21d5fbcac065a13e5fb936e9b304ff0bada45f2fac1" + }, + { + "file": "LABEL_AI GENERATED_black.svg", + "sha256": "503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f" + }, + { + "file": "LABEL_AI GENERATED_white transparent.svg", + "sha256": "d19736f6da9d38af3cffce8ba08f3f7658b573e1794c173fcd8b55a5c200ed44" + }, + { + "file": "LABEL_AI GENERATED_white.svg", + "sha256": "10125cdef3fc60a351df72fe1266bb00ea046086921bd1bb20135f0f07b5ed36" + }, + { + "file": "LABEL_AI MODIFIED_black transparent.svg", + "sha256": "9ab09f54c1ccef01799af43794cd93a8af6607ba9ac6b9526fb90a02be8ddf3d" + }, + { + "file": "LABEL_AI MODIFIED_black.svg", + "sha256": "2e7349e5eca4ee78eeef160dfef4545c31850932245373a07c5401e73e6599c0" + }, + { + "file": "LABEL_AI MODIFIED_white transparent.svg", + "sha256": "7ccbce41f9f821a574007b32806ddb6d635ee8bbc5d753ccd05f66f5a3165845" + }, + { + "file": "LABEL_AI MODIFIED_white.svg", + "sha256": "0c72a7d569a1447e2982a843e156bd5f9cb2ab6f201943c6c68f988dc2f1bb6e" + }, + { + "file": "LABEL_AI_black transparent.svg", + "sha256": "f8e20f1dde8c7d95940da08960b1063620812acc9b4ea96eb5ad051f80f67c21" + }, + { + "file": "LABEL_AI_black.svg", + "sha256": "58ffe859a4d74829d397f534a988081bcefb716849c7d3b84fa7a026dd1d257f" + }, + { + "file": "LABEL_AI_white transparent.svg", + "sha256": "52d87cb3f6a191dba24796f745e1eeb80d342883acd1a2e6d856342ad853766c" + }, + { + "file": "LABEL_AI_white.svg", + "sha256": "5b3b94ae67fea55c4f2d013d0b8ed5b876c1533555ed0ed60e688f2f3ccf1a50" + }, + { + "file": "LABEL_AI GENERATED_black transparent.png", + "sha256": "74c8c4ee720c4a3286c3c1b9f216ac9b918ebf449e8c421dde5a40cd216b27e6" + }, + { + "file": "LABEL_AI GENERATED_black.png", + "sha256": "8d0af57bc93ba3797042a4b56db757d85f924d9b0200b75b8842da7be5e402a2" + }, + { + "file": "LABEL_AI GENERATED_white transparent.png", + "sha256": "06c4c7c00738e0e44c35f5927e72842ff7a9dd757e0374335b9cc1ba6bb52199" + }, + { + "file": "LABEL_AI GENERATED_white.png", + "sha256": "f139d8de4b5173f47a777431e7e03d9258d8ea7ee1ee1a062febc7e9fff067fc" + }, + { + "file": "LABEL_AI MODIFIED_black transparent.png", + "sha256": "f48f6c5137834fff87c701eb9a44934ac51c6b5d826b9b5cfead1b88024f8387" + }, + { + "file": "LABEL_AI MODIFIED_black.png", + "sha256": "0f32e4afbe1459baaf425c78373ea541421ecdb61803438892e2fc07df67da8f" + }, + { + "file": "LABEL_AI MODIFIED_white transparent.png", + "sha256": "1f3b435b0bf775a168309c4ede49b23c7fa94b759a72623932e6ccb7bdef6203" + }, + { + "file": "LABEL_AI MODIFIED_white.png", + "sha256": "64241cf4f87eb0837bd234cb63716c7d78f4e9dbbb8866f40464ca4e6bfbfead" + }, + { + "file": "LABEL_AI_black transparent.png", + "sha256": "24da93847c04a5c86c714c6cd018c45331b47df32936b617b3c8476933d97fa9" + }, + { + "file": "LABEL_AI_black.png", + "sha256": "0814d5f04533c118f81aed0d353ea75d1df4171c40b962dba1dfa044a3d84779" + }, + { + "file": "LABEL_AI_white transparent.png", + "sha256": "5b8c743b679a62453a4bc25070b82bb4db33ad9c68f4ef1b6ef7bb562dd3a4e3" + }, + { + "file": "LABEL_AI_white.png", + "sha256": "b9041e416fed455722a3aaa06a5ea0eb376d8a8e11385fdc8e3e8ee6998cdf26" + } + ], + "vectors": [ + { + "name": "chain", + "about": "a capture, a neural codec's reconstruction of it, and two pinned integer derivations of that reconstruction, each recomputed from its input's decoded samples and proved (§3, §4)", + "policy": { + "dual_layer": "when-given", + "assistive": "require-proof" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"5b7f404099ea0a38ea23e2c5afd94204c52c6c1c538ef65f5f77492a276b235b\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"33fad4cc0ce6211b8998d6b5da2b6b1f95f309263c20204bc5a2666e05c777ca9983b2292eef37dd8be07c607f75f7ed2124f73e549ef410085ff56d3140fc01\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}", + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:denoise3\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"d4cd332c1efaf29d4be4430b587cc22e4dd07f9a92282a15fd33c4a7fba078bd\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:denoise3\",\"oversight\":null,\"record\":null,\"sig\":\"cead22a7570c027f963e4db55d492ba2988e57f5976504b62f77d2d1637f8b35ec044a3edaee2300195172e949a04029f239de44a7ddf66bcbac46c4b7fd8e03\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"bbeddbfbea7af91c2a151e23d18e24223bc5402d73438922f56d00d9cc9f3924\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [ + "57414931650000007b22776169223a22312e30222c226d65646961223a22696d616765222c22696e74656e74223a227265706c6963617465222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e696d6167652e706e67227d7d30000000adcacffa0e72ec5a3c7669181b08b5692cd12204987cc37e77269c1f412c0b9256006715ab5c2f20f8a0fdc97c291ff1", + "574149316c0000007b22776169223a22312e30222c226d65646961223a22696d616765222c22696e74656e74223a227265706c6963617465222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e6e657572616c2e696e745f6879706572227d7d28000000fe2b90bc63770f4fee58835d9961fa96dd35f8ef1b1d203d742fbe9b9c0762ff6a9fdda2fc33f61d", + "57414931650000007b22776169223a22312e30222c226d65646961223a22696d616765222c22696e74656e74223a227265706c6963617465222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e696d6167652e706e67227d7d30000000ec9cec6032704eb18efb2f3025c6806383037212d32f92949f3cd2e053fb6750925d43e109c5e6c16de7f2564dce6d1e", + "57414931650000007b22776169223a22312e30222c226d65646961223a22696d616765222c22696e74656e74223a227265706c6963617465222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e696d6167652e706e67227d7d3000000098703f795784510292cfa74ab16f4dffd69f99e7ec2c98d071220146b803a8015a945bf6b02b39ba9f81013a3b2ba2f5" + ], + "decoded": [ + { + "object": "5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2", + "samples": "8800f880a088b828c028586818104058b800c0b040e858f8f860e0e0e0f8d0e870f8b068" + }, + { + "object": "6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0", + "samples": "8800f88450c080a0889c64a4b828c070409428586828586850089c5e5a706cac44948c62bc6c80a262988858b08858b018104038642058b8008cb420c0b040d4849ce858f8e858f88838909282869ccc7cb2cc6ac8cc58dca884f084b0f084b0f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068" + }, + { + "object": "bbeddbfbea7af91c2a151e23d18e24223bc5402d73438922f56d00d9cc9f3924", + "samples": "6f2db185678e91658d7155917247898e8d71b1997abe7a9fc06fb9c2b5a4d3c984eaaa82" + } + ], + "room": null, + "outcome": "accept", + "statements": { + "claims": [ + { + "link": "e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31", + "class": "captured", + "source": "digitalCapture", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.created\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture\"}],\"assertions\":[]}", + "assistive": null + }, + { + "link": "4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb", + "class": "reconstructed", + "source": "digitalCapture", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.converted\"}],\"assertions\":[]}", + "assistive": null + }, + { + "link": "f1399393f092c48ba1058b0ffb43694fd6dfda3f943bffd4a1f2860e127a72e7", + "class": "assistive", + "source": "algorithmicallyEnhanced", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.edited\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicallyEnhanced\"}],\"assertions\":[]}", + "assistive": "proved" + }, + { + "link": "729b1bcdad65da15e543b465321304fb698a6a82d5da09e75bbd5eab2a4dbe89", + "class": "assistive", + "source": "algorithmicallyEnhanced", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.edited\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicallyEnhanced\"}],\"assertions\":[]}", + "assistive": "proved" + } + ], + "detections": [], + "ai_tracks": [], + "reviews_presented": [], + "labels": [], + "visual_icon": null, + "room": null + } + }, + { + "name": "assistive_asserted", + "about": "a float super-resolution step that states the assistive class with attest evidence: asserted, not proved, under a policy that accepts assertions (§4.4)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"super-resolution\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"e9aebdaebbd8ad1ef951b3d2d0cc62272cfcd73f1b69d11363d361826f458b2d\",\"params\":{\"scale\":2},\"tier\":\"attest\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[{\"digest\":\"e2ea8c07e5e2e2c846c97de6f5f79ab21593563603420e70736ab7f22ca451d4\",\"type\":\"trained\"}],\"operation\":\"super-resolution\",\"oversight\":null,\"record\":null,\"sig\":\"0cf70135dcca61696a1cf36b7dfff01475d0730495fad8c68e11a7b5d7eb62fff2be5345897dca3cb5c9b1ff0ca1ef7dde333a5d1dc0cabe7d34c246ce4b1f08\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"d4b33e2b9bad56645fcf0b7856b9009fecd2b4e5e587eeb791a0da87daa601ce\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "accept", + "statements": { + "claims": [ + { + "link": "e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31", + "class": "captured", + "source": "digitalCapture", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.created\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture\"}],\"assertions\":[]}", + "assistive": null + }, + { + "link": "4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb", + "class": "reconstructed", + "source": "digitalCapture", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.converted\"}],\"assertions\":[]}", + "assistive": null + }, + { + "link": "2d4695995c98f882d8f9371be30ba3d9fe745ef04e53adfda31c51e2070a6b43", + "class": "assistive", + "source": "algorithmicallyEnhanced", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.edited\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicallyEnhanced\"}],\"assertions\":[{\"data\":{\"modelIdentifier\":\"tag:transaction.science,2026:wai/model/e2ea8c07e5e2e2c846c97de6f5f79ab21593563603420e70736ab7f22ca451d4\",\"modelType\":\"c2pa.types.model\"},\"label\":\"c2pa.ai-disclosure\"}]}", + "assistive": "asserted" + } + ], + "detections": [], + "ai_tracks": [], + "reviews_presented": [], + "labels": [], + "visual_icon": null, + "room": null + } + }, + { + "name": "generated", + "about": "a generated video, a pinned algorithmic audio generation, an inpainted capture reviewed by a person, a composite of a capture and the generated video, and a transcode of it; each synthesized object marked in both layers, a detection agreeing with the video's claim, and one finding no mark on the capture (§5, §6, §7)", + "policy": { + "dual_layer": "required", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}", + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"audio\",\"models\":[{\"digest\":\"b47ce1b548507dcf199243a31dec452a7674e16181cbc6aa5c9d6ce8e729568e\",\"type\":\"algorithmic\"}],\"operation\":\"wai.generate.spectral\",\"oversight\":\"prompt_guided\",\"record\":null,\"sig\":\"93bf400b641704b0bc8599714bd9df962908c842cc9ec807fc74b8c3e44285e259d6c54b616a076e68ea8f8725e6bdb5792a804fb0082ddc0cc19b295b596a08\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"algorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"4df75d76e850e6e24b6c19a3c6ca3e8fc6f14ad5029e80ba05c4076baa07a917\",\"type\":\"object\"}}", + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[{\"digest\":\"8cf24466f8c423296347a21fe427b09a94beb668ba22aac59a5173abf4a9517c\",\"type\":\"trained\"}],\"operation\":\"inpainting\",\"oversight\":\"human_validated\",\"record\":null,\"sig\":\"ab65ae0639749dd7ed50886934677d969d33cd53567ca7bd2d07f4fb8f2563313e8ba5aef66a5588a25582ac2034ed31b6d9b4b81fad050dd4fa0c01ca1ee20b\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"compositeWithTrainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"501e6fb06de91e323282c5a8afd400d21adcabd99576bbc7a8b6b5fccf05524a\",\"type\":\"object\"}}", + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"},{\"class\":\"synthesized\",\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"source\":\"trainedAlgorithmicMedia\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.derive.overlay_composition\",\"oversight\":null,\"record\":null,\"sig\":\"d66cb56f05da99a765ae25b5f65464a70c4295ae8f091c4d8f9ae93a781439245451a8c540b8790c0f56ed1bc2215efd0bc556430a45e67ccb97476476998107\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"compositeSynthetic\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"19db0b61e5caf49013bb45bed4e9d77f80abe99a4957e3b9a758c87a47ed41a1\",\"type\":\"object\"}}", + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[{\"class\":\"synthesized\",\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"source\":\"trainedAlgorithmicMedia\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[],\"operation\":\"wai.video.hevc\",\"oversight\":null,\"record\":null,\"sig\":\"1b766b2c66a402d8f0a5d3a79fd2a576060781f603e0f6b105a5400bc6b1358c1c096d2d51fe8fa469b7367907fd1cff54cfaa9e4b1000fccb89ea41e8b63a0f\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"c61cb9797fc8bd007459550e2e21ab4989c36d3b26f4d7d10773dd03184621cd\",\"type\":\"object\"}}" + ], + "markings": [ + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-marking\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"payload\":\"03ead74abe77e324\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"1c6c99343e99185ae70d5c0d496628cbaa7e00bd50c6539f1a658ab82e663cbe9ea2302b9c125db6005fa5da8eb837f23f843f0e26af975ca63b794c8942610a\",\"signer_pubkey\":\"a56ef30c04f5b691ee3a2f648d5f5036cfed6b7db6514a23122c75e02deccf7c\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\"}", + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-marking\",\"origin\":\"08c12d093fe7df981379a7bd0ba70f8b9fb490a705c987e1ed13f98b16b23663\",\"payload\":\"0308c12d093fe7df\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"551cebcec062e1e13ce1def1f411e4be23f1462ee02abcc2b847bd456a94ff65b7013e7bf0922f1c1239f6b6a9afd9f830cfa13a3d545d914848668c43e01f0e\",\"signer_pubkey\":\"a56ef30c04f5b691ee3a2f648d5f5036cfed6b7db6514a23122c75e02deccf7c\",\"subject\":\"4df75d76e850e6e24b6c19a3c6ca3e8fc6f14ad5029e80ba05c4076baa07a917\"}", + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-marking\",\"origin\":\"e7a086610eb064e991d86cf59ca2239c6a1cadda6e824140efe8c1530898aef5\",\"payload\":\"03e7a086610eb064\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"ac99705a34d7f3c76e50f8ed8a18d4d30dfe59230220561bae5bb581656bbedc77b1bc343af487db21c68f5028b0399289818fcaa8fc3128f3de62e62ffe330e\",\"signer_pubkey\":\"a56ef30c04f5b691ee3a2f648d5f5036cfed6b7db6514a23122c75e02deccf7c\",\"subject\":\"501e6fb06de91e323282c5a8afd400d21adcabd99576bbc7a8b6b5fccf05524a\"}", + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-marking\",\"origin\":\"9dec524cf50ebf41ac92d3531d657d30ffe0b9c68c14aed8641b1a5c6affb014\",\"payload\":\"039dec524cf50ebf\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"0569b219a150db512882b331bf4a7fb17aee13c9494904f5f783cbf6bf405a98a68f56574b6bd11a5e41d3f463ca4206d60eae8d31cdb8f7019a9ab92e4dff0a\",\"signer_pubkey\":\"a56ef30c04f5b691ee3a2f648d5f5036cfed6b7db6514a23122c75e02deccf7c\",\"subject\":\"19db0b61e5caf49013bb45bed4e9d77f80abe99a4957e3b9a758c87a47ed41a1\"}", + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-marking\",\"origin\":\"bbf4d18b4bdf0851e5bcdf02d2ef994a8cdece8e4f160b6cd25a0d4bc3f1a14d\",\"payload\":\"03bbf4d18b4bdf08\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"b209591736654bb122938823a1aa1ad6a10c6e09cf12494a67be06175ddb7a06ddf1c31495c8ef054d6a35e48f9eef4f3c961e9730b3ea59226d79c0feffc20b\",\"signer_pubkey\":\"a56ef30c04f5b691ee3a2f648d5f5036cfed6b7db6514a23122c75e02deccf7c\",\"subject\":\"c61cb9797fc8bd007459550e2e21ab4989c36d3b26f4d7d10773dd03184621cd\"}" + ], + "detections": [ + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-detection\",\"payload\":\"03ead74abe77e324\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"f10cf6cdf66a82d84042dc410bda131a98f5ef21730b4d56ee473ebf3c599efdcf78850458060f68db43054b270e149ffc9af1f113035286af3f25f33067c40f\",\"signer_pubkey\":\"a4ddb8268591a53a9ee6446a9ee8d5917fa7b337a65f806b9d12708cf19322c6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\"}", + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-detection\",\"payload\":null,\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"af7d6d6160203509f907d0f592cba7b2199cd9fa58f2c8d282273c4511cfd525e0a9f36a7bbd96141bb126bb5985f1e3f3f25626005aa5988a9906c98fd2b604\",\"signer_pubkey\":\"a4ddb8268591a53a9ee6446a9ee8d5917fa7b337a65f806b9d12708cf19322c6\",\"subject\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\"}" + ], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [ + "57414931650000007b22776169223a22312e30222c226d65646961223a22696d616765222c22696e74656e74223a227265706c6963617465222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e696d6167652e706e67227d7d30000000adcacffa0e72ec5a3c7669181b08b5692cd12204987cc37e77269c1f412c0b9256006715ab5c2f20f8a0fdc97c291ff1", + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53", + "574149316a0000007b22776169223a22312e30222c226d65646961223a22617564696f222c22696e74656e74223a22637265617465222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e67656e65726174652e737065637472616c227d7d0a0000007b22666f726d223a307d", + "57414931650000007b22776169223a22312e30222c226d65646961223a22696d616765222c22696e74656e74223a227265706c6963617465222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e696d6167652e706e67227d7d3000000019e6efa99b020862b987bc8b1f342201b75f1dcbf6957eff0cc3f89482b7d9a5009a19d526c1a75e9ef3d303b8baaa94", + "57414931650000007b22776169223a22312e30222c226d65646961223a22696d616765222c22696e74656e74223a227265706c6963617465222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e696d6167652e706e67227d7d30000000b63b86d4ec9dd56c4dac4146be2804b51b5869d862a98670ae00d52261c72ac3f1df18647863e8cdeabff46339b9c24c", + "57414931670000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e68657663227d7d4000000094e921099bea0c893bce9d14f037d535670e1f3f3e04ef819bb5aaa20f70edac4d3d7518e875ab1bc990c7524dcc4d6731add333db596ef78a24984f9cd87bb8" + ], + "decoded": [], + "room": null, + "outcome": "accept", + "statements": { + "claims": [ + { + "link": "e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31", + "class": "captured", + "source": "digitalCapture", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.created\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture\"}],\"assertions\":[]}", + "assistive": null + }, + { + "link": "ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0", + "class": "synthesized", + "source": "trainedAlgorithmicMedia", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.created\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia\"}],\"assertions\":[{\"data\":{\"contentProfile\":{\"humanOversightLevel\":\"fully_autonomous\"},\"modelIdentifier\":\"tag:transaction.science,2026:wai/model/1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"modelType\":\"c2pa.types.model\"},\"label\":\"c2pa.ai-disclosure\"}]}", + "assistive": null + }, + { + "link": "08c12d093fe7df981379a7bd0ba70f8b9fb490a705c987e1ed13f98b16b23663", + "class": "synthesized", + "source": "algorithmicMedia", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.created\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicMedia\"}],\"assertions\":[]}", + "assistive": null + }, + { + "link": "e7a086610eb064e991d86cf59ca2239c6a1cadda6e824140efe8c1530898aef5", + "class": "synthesized", + "source": "compositeWithTrainedAlgorithmicMedia", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.edited\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/compositeWithTrainedAlgorithmicMedia\"}],\"assertions\":[{\"data\":{\"contentProfile\":{\"humanOversightLevel\":\"human_validated\"},\"modelIdentifier\":\"tag:transaction.science,2026:wai/model/8cf24466f8c423296347a21fe427b09a94beb668ba22aac59a5173abf4a9517c\",\"modelType\":\"c2pa.types.model\"},\"label\":\"c2pa.ai-disclosure\"}]}", + "assistive": null + }, + { + "link": "9dec524cf50ebf41ac92d3531d657d30ffe0b9c68c14aed8641b1a5c6affb014", + "class": "synthesized", + "source": "compositeSynthetic", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.created\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/compositeSynthetic\"}],\"assertions\":[]}", + "assistive": null + }, + { + "link": "bbf4d18b4bdf0851e5bcdf02d2ef994a8cdece8e4f160b6cd25a0d4bc3f1a14d", + "class": "synthesized", + "source": "trainedAlgorithmicMedia", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.converted\"}],\"assertions\":[]}", + "assistive": null + } + ], + "detections": [ + { + "subject": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "result": "agree" + }, + { + "subject": "69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681", + "result": "unmarked" + } + ], + "ai_tracks": [], + "reviews_presented": [], + "labels": [], + "visual_icon": null, + "room": null + } + }, + { + "name": "ai_room", + "about": "a room whose AI host's grant names its model: its chat and caption state that model, and its published speech track has a synthesized track claim naming it (§8)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"audio\",\"models\":[{\"digest\":\"857f14d62ade478f90d495a17495e1aa6608252205fe1b4e0bd43dfcf1456761\",\"type\":\"trained\"}],\"operation\":\"speech-synthesis\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"7e7b5b7821bc7928d1e8ae3ae2e25b2149ed3a624a6217db003658b9890afd9373c61d7d4d68e8dceb3664ee077006397b95ee3d2e0171caf73bc66bd6edfc09\",\"signer_pubkey\":\"26e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e741\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"from_group\":0,\"track\":\"e0f5f40db551bfb20f862d5d6ffd64dbea588d2181d8dfe58849b4d9f15931a6\",\"type\":\"track\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": { + "descriptor": "5752443101ba07665226a6cc29262167d963816c0949855436567469a282299e19951d15d86d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc400406cd8e95d060040420f00000000000000080000000200000000005745503101fe1f0000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f00000000000204686f73740101000008040000000000000000000000000000000000000000000000000000000000000861756469656e63650001000018000000000000000000000000000000000000000000000000000000000000000001067370656563680101046f707573000001000000000000100000004000000080bb0000010f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008951f673cfa31eaf7f5b68c7755826574bdf858e85925f8ab4ae2122db97b97ce4f0359e91ccefc8caec38d6ce9b2e90d070141e68d23503d93ba41a40c6b904", + "grants": [ + "5752473101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e126e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e74100000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000001010000000000000000000000000000000000000000000000000000000000000000000000574550310108040000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff0000000100011568747470733a2f2f72656c61792e6578616d706c65450002616940003835376631346436326164653437386639306434393561313734393565316161363630383235323230356665316234653062643433646663663134353637363173f388e6e8845b7c383256ed852a5d354251fac217b9aa5cf1f2021719c1dede958544a127652754264fdf2379ff4dabeb0afe0862afc9674b314239a7ccc10a", + "5752473101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e1332dd253be02fa4f9d861626e9e09ad19786534665f684b833f5beaa94075f5400000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000100010000000000000000000000000000000000000000000000000000000000000000000000574550310118000000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c6500001903d5c5c1f8f10ba284c91c71382d59f316181816ae9a2bb9e232a1fcf7cc40000afe69527553f25c996d3bd3ab0392be40c1baaafbb83ed8b1488207213206" + ], + "key_log": null, + "chains": [ + [ + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e16d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010400026e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e7417b98c0c1a0e637327a2d6d5f5fd46e63bb2a5970f6c67586cd7de6f575b6360f92c58afd19b1590b027705e137e7fd9113ec862f4a46f7cf98af22cddf15f6ab0a3d797c19ac7bac710280be04e85315ebb15d4a498b376ba1cf4fa5ef760d0a", + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e16d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc40100000000000000e30951a58b352a71167f2653d553a499f42535929d2c70a2df6707d9237813e9000000000000000000104000332dd253be02fa4f9d861626e9e09ad19786534665f684b833f5beaa94075f5469e9ac354d85ed0e33615f6021464551a549219747f1887e8bd0ba8e198a417289544d1a426e53d808c6e39e490a36f4d7bbbb293038f456841cfe524fb294695727a8c1e1261c0274f4268369b32026b31ae73bfadb3f5c1fdc72341ac65604", + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e16d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc40200000000000000f01481d18e4abd33b4feb973fad90407428633775ee248a3ee1ba5804452b24740420f00000000000012240026e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e741000100010011eec15a7256a01e340ad71d4ca262cbbd3b5d14b89767610b584fe8a879aaf56e8d567369bc64313dc8857a695c44935af619b0ebb63e9a4fbc1d8847130e" + ], + [ + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e1332dd253be02fa4f9d861626e9e09ad19786534665f684b833f5beaa94075f540000000000000000000000000000000000000000000000000000000000000000000000000000000080841e000000000000030800060048656c6c6f3f53e502ef3d4f09c4a615c8374bc433e727bf8bee9a34083ee5ba47ba2bbe463e96959334a02fd78310e063b5ee409626258cbf429e480c30b85f82349c13890a" + ], + [ + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e126e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e74100000000000000000000000000000000000000000000000000000000000000000000000000000000c0c62d000000000000034600230048656c6c6f2e204920616d20616e206175746f6d6174656420617373697374616e742e02857f14d62ade478f90d495a17495e1aa6608252205fe1b4e0bd43dfcf1456761290bea86e3e37ec17ddaa56e7eb3021ac46aca24e23452fb610549bb3599587822fbc702af39ca05d47e519f6a5bf1aecb37e28b2c784abce201d54026a70105", + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e126e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e741010000000000000052c8b1b099988b7700357fdc8c43b4fb67a8174eab58d664b5490ae5a29ec70200093d0000000000000a3000d007000002857f14d62ade478f90d495a17495e1aa6608252205fe1b4e0bd43dfcf145676102656e060048656c6c6f2ebca90ce1ee985ff5ab8d0c6d4e88fed39c76116454f6ce251b82f9b311cff3eb4440ffd246a25255b26a36529eeae05baab1f489ec4f22cc2cce45ce3bc6d306" + ] + ] + }, + "outcome": "accept", + "statements": { + "claims": [ + { + "link": "850549908658ec3921abee64bb94fcfad402b22efcf1b0d6915ae61bf15f8738", + "class": "synthesized", + "source": "trainedAlgorithmicMedia", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.created\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia\"}],\"assertions\":[{\"data\":{\"contentProfile\":{\"humanOversightLevel\":\"fully_autonomous\"},\"modelIdentifier\":\"tag:transaction.science,2026:wai/model/857f14d62ade478f90d495a17495e1aa6608252205fe1b4e0bd43dfcf1456761\",\"modelType\":\"c2pa.types.model\"},\"label\":\"c2pa.ai-disclosure\"}]}", + "assistive": null + } + ], + "detections": [], + "ai_tracks": [ + { + "track": "e0f5f40db551bfb20f862d5d6ffd64dbea588d2181d8dfe58849b4d9f15931a6", + "claim": "850549908658ec3921abee64bb94fcfad402b22efcf1b0d6915ae61bf15f8738" + } + ], + "reviews_presented": [], + "labels": [], + "visual_icon": null, + "room": "accept" + } + }, + { + "name": "certified", + "about": "a review of a generated video at decode-equivalence, presented by a player whose decode path holds that tier and whose output is the reconstruction reviewed; the label shown at first exposure, after an ad break and within the interval (§9, §10)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": "{\"audio\":\"6e0d3d5e9f9661b8c2327e57ebe1b360b204e109def81020a485edb8bf2f58e8\",\"classes\":[\"synthesized\"],\"grace_us\":500000,\"interval_us\":30000000,\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-label-policy\",\"sig\":\"50693722b2490992604330c305b695ac8ce99b2e4ac291dadbedcc9a0d9e128f965ee347a360675481d441f67145b5dd22417345274b9b337953ebb23479d300\",\"signer_pubkey\":\"34e2cdfcf44eaae9f3974cd801473ae99e6779b7e3d432fc12ff35f26d429948\",\"visual\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\"}", + "player": [ + "{\"at\":0,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"first-exposure\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"1071b9e5bc259e948b29c4b6e79c76a4749000d0998f97bb218160faac47a925d8df36ed6fe62a3586c8adae3c3ba32dbc21ad1434bed9c3c00c424b1962950f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"d43d753b42fa00b30b4bde2820034298f165e27cf9b1b1b8c78e99131e1c8d8a\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"fe343689ded585d0cb6c9b78e508152dd3f537125c2634bdc0c53b53402d7db14af0aac5431c8a24059d5f005d8d501f1cd68d1ff2bfa62fde8858a945506f01\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":1000000,\"entry\":{\"review\":\"f53388638eec4d3c50ad26873a266d51a5468f2b9df922e30d93e3980fa60282\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"review\"},\"kind\":\"wai-player\",\"prev\":\"780d0b5b9cb2a9439625ed2ae4cd88fa58078cd8faf9a93ce698f415467a79a3\",\"seq\":2,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"b3b382c0944fb868922b89d145974a98f62133a5a6053492709c4e1fc80ffc35184daf10df5414984f93fb345383fc93cc0247e80f0d0dcd06e4fa504dfac800\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20000000,\"entry\":{\"after\":\"ad-break\",\"type\":\"resume\"},\"kind\":\"wai-player\",\"prev\":\"8798772ab497744055c606abf9512a977710114b1d2e823fe8feb41e87ff94b1\",\"seq\":3,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"07605ee164d8b229cc90d97cee85ae4f16024b2ebd8d4ed36a1b2cd71f3c6b0dbbed9c2bbe2aecd99047c0f9bce48dbcf22fab06121382c3bfbd750400b89b09\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"resume\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":\"e74865cf7556384fb5f978b1c89baa512762d79a4dda6e5fd3de460f211a46be\",\"seq\":4,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"082fb36c2f63a351bd2e6a5a1d697f300cda5a33405d08bf42a9d79f2c1e2579735d4ab1dd46cb1e2d51c25ab601135117df90ebdc34d61516cbcabf4798d802\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"0ce999210725d04111c829732c363e5587c1a13eb6990a74bf6ab09b3e4faa8f\",\"seq\":5,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"66ba5826145a6d7a17ed89e8bf8faa725bb34942afb3e86046a4d7380c57f8e7fec3bb6d3e55d01e76927065f13536b86a061eec0ba776de732462e61ed6940d\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":40000000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"4c772443e1c5b98c79ff193646ec9ee9ef00062f0bb8ab89330d08088e281686\",\"seq\":6,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"4429d3e146c5f24ad739037848fe8531a5978b062632351c231b5a827e9463b4390143db6d7a4acfe9b2f0234dd4826baa60930422ab2af65746d34716ca1a0f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "accept", + "statements": { + "claims": [ + { + "link": "ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0", + "class": "synthesized", + "source": "trainedAlgorithmicMedia", + "c2pa": "{\"actions\":[{\"action\":\"c2pa.created\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia\"}],\"assertions\":[{\"data\":{\"contentProfile\":{\"humanOversightLevel\":\"fully_autonomous\"},\"modelIdentifier\":\"tag:transaction.science,2026:wai/model/1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"modelType\":\"c2pa.types.model\"},\"label\":\"c2pa.ai-disclosure\"}]}", + "assistive": null + } + ], + "detections": [], + "ai_tracks": [], + "reviews_presented": [ + { + "entry": 2, + "review": "f53388638eec4d3c50ad26873a266d51a5468f2b9df922e30d93e3980fa60282" + } + ], + "labels": [ + { + "entry": 1, + "trigger": "first-exposure", + "shown": 0 + }, + { + "entry": 5, + "trigger": "resume", + "shown": 4 + }, + { + "entry": 6, + "trigger": "interval", + "shown": 4 + } + ], + "visual_icon": "LABEL_AI GENERATED_black.svg", + "room": null + } + }, + { + "name": "record_malformed", + "about": "an origin claim with a member §3.1 does not list", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"note\":\"an extra member\",\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "record_malformed", + "at": [ + "claim", + 0 + ] + }, + { + "name": "record_malformed_step", + "about": "a capture that names an input: a capture has none (§3.2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"0bdc921c0bff9e00a34c2ff518feee86373a682472262ca386b9907cb4b8d271a1c3a8350b2b8e11e467346de7eba1bd944825b8d2632f133016410dbd785507\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "record_malformed", + "at": [ + "claim", + 0 + ] + }, + { + "name": "bad_signature", + "about": "an origin claim whose signature has one digit changed", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"00a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "bad_signature", + "at": [ + "claim", + 0 + ] + }, + { + "name": "bad_key", + "about": "an origin claim whose key is the identity point, of small order (SPEC §7 *Signatures* step 2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"0100000000000000000000000000000000000000000000000000000000000000\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "bad_key", + "at": [ + "claim", + 0 + ] + }, + { + "name": "claim_conflict", + "about": "two origin claims on one object", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T10:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"5c2b64f7b8a7035ef656f79daf42493c77491bfd036a80705ef217670a413a327f7960893782cd48a2b347021c0234fc53b48cc69de7a2eaefd6c574303ca505\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "claim_conflict", + "at": [ + "claim", + 1 + ] + }, + { + "name": "class_mismatch", + "about": "a transcode of a generated video that states the class reconstructed: a conversion keeps its input's class (§3.2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"synthesized\",\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"source\":\"trainedAlgorithmicMedia\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[],\"operation\":\"wai.video.hevc\",\"oversight\":null,\"record\":null,\"sig\":\"bf6e0e7da44528f7036d1901604b9a9f385de17848ba725f89c186a7241749e51b1888e41093401bd70cacbddf1106529153e9dec8d72423967eb2b1ce5c6d07\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"c61cb9797fc8bd007459550e2e21ab4989c36d3b26f4d7d10773dd03184621cd\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "class_mismatch", + "at": [ + "claim", + 1 + ] + }, + { + "name": "source_mismatch", + "about": "a codec's reconstruction of a capture that states digitalCreation: a conversion keeps its input's source (§5)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"54c54b2c87c00dddef409f18d9fc44eb99000f65824d7c707a7505e95d34e040823845d7d482ffffaa5e1891b3eacfe6f745ed07edccef99e3bbd7938926800b\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCreation\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "source_mismatch", + "at": [ + "claim", + 1 + ] + }, + { + "name": "input_mismatch", + "about": "a reconstruction that restates its input's class as reconstructed where the input's own claim says captured (§3.3)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"22f75bacc7e121dac69c6c24b5654a2b4323e23d768d040df8115eddf02cccd723bd7438071d7189c9dd85d7728c76b0177f6d8fe860fd44858a238354e68405\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "input_mismatch", + "at": [ + "claim", + 1 + ] + }, + { + "name": "class_understated", + "about": "a synthesized object labelled captured: the object is a generator's envelope (wai.generate.spectral, intent create), and its claim is a capture (§3.3)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"716ff62326cbb6e07cc61705672bac858cdadaf916bd05c49e66e7b28bef058e180d507460a0db3faa7dfed11eda5ed50ef6a27884024688218ef15d9869470d\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"4df75d76e850e6e24b6c19a3c6ca3e8fc6f14ad5029e80ba05c4076baa07a917\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [ + "574149316a0000007b22776169223a22312e30222c226d65646961223a22617564696f222c22696e74656e74223a22637265617465222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e67656e65726174652e737065637472616c227d7d0a0000007b22666f726d223a307d" + ], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "class_understated", + "at": [ + "claim", + 0 + ] + }, + { + "name": "evidence_invalid", + "about": "replicate evidence naming a derivation §4 does not pin", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale3\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"5b7f404099ea0a38ea23e2c5afd94204c52c6c1c538ef65f5f77492a276b235b\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"8b4a1b88633cef43d4fc4de5be5d1b89c8454cf31c1841687d4ed4c0860ac38f92f54c8e4d2e18fd73eea3b5b2d94c7b50d852103de49a133a46d8b6585bed02\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "evidence_invalid", + "at": [ + "claim", + 2 + ] + }, + { + "name": "input_samples_mismatch", + "about": "an upscale whose evidence names the capture's samples, not those its input decodes to (§4.4)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"83a041d31be9a9e43824a8cad0d33ff64fc41d0c359ce9414bf2abda7d7b4698\",\"output_samples\":\"5b7f404099ea0a38ea23e2c5afd94204c52c6c1c538ef65f5f77492a276b235b\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"702489474ef131b989adb9b612af74f0942c8040cb5dde81d6f34fdc03352fac87f21bae59b3d83e2958c2f409f4e366055bcca1abf3fbe38799ea14c5e72002\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [ + { + "object": "5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2", + "samples": "8800f880a088b828c028586818104058b800c0b040e858f8f860e0e0e0f8d0e870f8b068" + }, + { + "object": "6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0", + "samples": "8800f88450c080a0889c64a4b828c070409428586828586850089c5e5a706cac44948c62bc6c80a262988858b08858b018104038642058b8008cb420c0b040d4849ce858f8e858f88838909282869ccc7cb2cc6ac8cc58dca884f084b0f084b0f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068" + }, + { + "object": "bbeddbfbea7af91c2a151e23d18e24223bc5402d73438922f56d00d9cc9f3924", + "samples": "6f2db185678e91658d7155917247898e8d71b1997abe7a9fc06fb9c2b5a4d3c984eaaa82" + } + ], + "room": null, + "outcome": "refuse", + "reason": "input_samples_mismatch", + "at": [ + "claim", + 2 + ] + }, + { + "name": "assistive_recompute_mismatch", + "about": "an assistive claim whose recomputation differs: the delivered upscale has one sample altered after the derivation, and its evidence states the altered samples (§4.4)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"ca2b468d7dfbe78a0184b49537c2955e5670239e18a2f36d0c1f44f313d59bdb\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"bc02fb2fe9564590405af2d8a400371cd4578dc53794f86fa4fe6824e77458b3135ed776beecc4ab445c5d6c15934aebf97f3f77a6c7263069ff4dfdeb2d840b\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [ + { + "object": "5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2", + "samples": "8800f880a088b828c028586818104058b800c0b040e858f8f860e0e0e0f8d0e870f8b068" + }, + { + "object": "6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0", + "samples": "8800f88450c080e0889c64a4b828c070409428586828586850089c5e5a706cac44948c62bc6c80a262988858b08858b018104038642058b8008cb420c0b040d4849ce858f8e858f88838909282869ccc7cb2cc6ac8cc58dca884f084b0f084b0f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068" + } + ], + "room": null, + "outcome": "refuse", + "reason": "assistive_recompute_mismatch", + "at": [ + "claim", + 2 + ] + }, + { + "name": "samples_mismatch", + "about": "an upscale whose evidence recomputes, delivered as an object that decodes to other samples (§4.4)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"5b7f404099ea0a38ea23e2c5afd94204c52c6c1c538ef65f5f77492a276b235b\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"33fad4cc0ce6211b8998d6b5da2b6b1f95f309263c20204bc5a2666e05c777ca9983b2292eef37dd8be07c607f75f7ed2124f73e549ef410085ff56d3140fc01\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [ + { + "object": "5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2", + "samples": "8800f880a088b828c028586818104058b800c0b040e858f8f860e0e0e0f8d0e870f8b068" + }, + { + "object": "6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0", + "samples": "8800f88450c080e0889c64a4b828c070409428586828586850089c5e5a706cac44948c62bc6c80a262988858b08858b018104038642058b8008cb420c0b040d4849ce858f8e858f88838909282869ccc7cb2cc6ac8cc58dca884f084b0f084b0f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068" + } + ], + "room": null, + "outcome": "refuse", + "reason": "samples_mismatch", + "at": [ + "claim", + 2 + ] + }, + { + "name": "assistive_not_proved", + "about": "a float super-resolution step with attest evidence, under a policy that requires every assistive claim to be proved (§4.4, §11.1)", + "policy": { + "dual_layer": "when-given", + "assistive": "require-proof" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", + "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"super-resolution\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"e9aebdaebbd8ad1ef951b3d2d0cc62272cfcd73f1b69d11363d361826f458b2d\",\"params\":{\"scale\":2},\"tier\":\"attest\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[{\"digest\":\"e2ea8c07e5e2e2c846c97de6f5f79ab21593563603420e70736ab7f22ca451d4\",\"type\":\"trained\"}],\"operation\":\"super-resolution\",\"oversight\":null,\"record\":null,\"sig\":\"0cf70135dcca61696a1cf36b7dfff01475d0730495fad8c68e11a7b5d7eb62fff2be5345897dca3cb5c9b1ff0ca1ef7dde333a5d1dc0cabe7d34c246ce4b1f08\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"d4b33e2b9bad56645fcf0b7856b9009fecd2b4e5e587eeb791a0da87daa601ce\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "assistive_not_proved", + "at": [ + "claim", + 2 + ] + }, + { + "name": "claim_unknown", + "about": "a marking whose origin claim the verifier is not given (§7.3)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}" + ], + "markings": [ + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-marking\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"payload\":\"03ead74abe77e324\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"1c6c99343e99185ae70d5c0d496628cbaa7e00bd50c6539f1a658ab82e663cbe9ea2302b9c125db6005fa5da8eb837f23f843f0e26af975ca63b794c8942610a\",\"signer_pubkey\":\"a56ef30c04f5b691ee3a2f648d5f5036cfed6b7db6514a23122c75e02deccf7c\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\"}" + ], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "claim_unknown", + "at": [ + "marking", + 0 + ] + }, + { + "name": "marking_mismatch", + "about": "a marking whose payload carries the class captured for a synthesized claim (§7.2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [ + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-marking\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"payload\":\"00ead74abe77e324\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"9d6ac7d8fa7777a8482722dde6801e7961a7104676de2585aff18895d46621b632001e912ad4fdde125dacf5bb8c9536ad762cd0d41fd9c32eda1c55e2b57205\",\"signer_pubkey\":\"a56ef30c04f5b691ee3a2f648d5f5036cfed6b7db6514a23122c75e02deccf7c\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\"}" + ], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "marking_mismatch", + "at": [ + "marking", + 0 + ] + }, + { + "name": "detection_mismatch", + "about": "a detection under another scheme definition than the one the marking names (§7.3)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [ + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-marking\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"payload\":\"03ead74abe77e324\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"1c6c99343e99185ae70d5c0d496628cbaa7e00bd50c6539f1a658ab82e663cbe9ea2302b9c125db6005fa5da8eb837f23f843f0e26af975ca63b794c8942610a\",\"signer_pubkey\":\"a56ef30c04f5b691ee3a2f648d5f5036cfed6b7db6514a23122c75e02deccf7c\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\"}" + ], + "detections": [ + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-detection\",\"payload\":\"03ead74abe77e324\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"5dcbb4c23056dc3f420b43a59f490d5cb1082a48bb276f75977cd52f2e5cafc7\",\"sig\":\"b8e07dee2e8c029a288514a1b3e1cfbe0c8071b3cd3780aa10d41c7d3311efae2a02c9fa63591581b7eaba8faca7bc306d38a3cb5733daaa55ebcf38fac0b800\",\"signer_pubkey\":\"a4ddb8268591a53a9ee6446a9ee8d5917fa7b337a65f806b9d12708cf19322c6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\"}" + ], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "detection_mismatch", + "at": [ + "detection", + 0 + ] + }, + { + "name": "watermark_absent", + "about": "a marked object in which the detector finds no mark: the second layer was stripped (§7.3)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [ + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-marking\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"payload\":\"03ead74abe77e324\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"1c6c99343e99185ae70d5c0d496628cbaa7e00bd50c6539f1a658ab82e663cbe9ea2302b9c125db6005fa5da8eb837f23f843f0e26af975ca63b794c8942610a\",\"signer_pubkey\":\"a56ef30c04f5b691ee3a2f648d5f5036cfed6b7db6514a23122c75e02deccf7c\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\"}" + ], + "detections": [ + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-detection\",\"payload\":null,\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"25bf74671a41233c3d263f9789dfc6146c78099f01775ad9026f903524c68ef13eb177de5eac541f4dd48fad7800d14a5f2bc3dd5ea2eb271f43f26ac6fae208\",\"signer_pubkey\":\"a4ddb8268591a53a9ee6446a9ee8d5917fa7b337a65f806b9d12708cf19322c6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\"}" + ], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "watermark_absent", + "at": [ + "detection", + 0 + ] + }, + { + "name": "layers_disagree", + "about": "the two marking layers disagree: the photo's claim says captured, and the detector reads from it a synthesized mark that points at another claim (§7.3)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [ + "{\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-detection\",\"payload\":\"03ead74abe77e324\",\"scheme\":\"wai-mark:conformance\",\"scheme_digest\":\"73ab84afa47d6646e97f18a6bbfebad6f924a6f1ae6f5d78532d727d975acbf3\",\"sig\":\"fe270dbd3df0b210b24468692f42a5a39c5f6038c9b2371c78beb91e7bd636cbd147f6bc6120b3ec417de5f27caea2e8f9f27b43884cf04dec39f55301f7370e\",\"signer_pubkey\":\"a4ddb8268591a53a9ee6446a9ee8d5917fa7b337a65f806b9d12708cf19322c6\",\"subject\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\"}" + ], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "layers_disagree", + "at": [ + "detection", + 0 + ] + }, + { + "name": "layer_missing", + "about": "a synthesized object with no marking, under a policy that requires both layers (§7.1)", + "policy": { + "dual_layer": "required", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "layer_missing", + "at": [ + "claim", + 0 + ] + }, + { + "name": "ai_track_unlabelled", + "about": "an AI participant publishes speech with no synthesized track claim for it (§8.2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": { + "descriptor": "5752443101ba07665226a6cc29262167d963816c0949855436567469a282299e19951d15d86d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc400406cd8e95d060040420f00000000000000080000000200000000005745503101fe1f0000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f00000000000204686f73740101000008040000000000000000000000000000000000000000000000000000000000000861756469656e63650001000018000000000000000000000000000000000000000000000000000000000000000001067370656563680101046f707573000001000000000000100000004000000080bb0000010f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008951f673cfa31eaf7f5b68c7755826574bdf858e85925f8ab4ae2122db97b97ce4f0359e91ccefc8caec38d6ce9b2e90d070141e68d23503d93ba41a40c6b904", + "grants": [ + "5752473101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e126e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e74100000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000001010000000000000000000000000000000000000000000000000000000000000000000000574550310108040000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff0000000100011568747470733a2f2f72656c61792e6578616d706c65450002616940003835376631346436326164653437386639306434393561313734393565316161363630383235323230356665316234653062643433646663663134353637363173f388e6e8845b7c383256ed852a5d354251fac217b9aa5cf1f2021719c1dede958544a127652754264fdf2379ff4dabeb0afe0862afc9674b314239a7ccc10a", + "5752473101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e1332dd253be02fa4f9d861626e9e09ad19786534665f684b833f5beaa94075f5400000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000100010000000000000000000000000000000000000000000000000000000000000000000000574550310118000000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c6500001903d5c5c1f8f10ba284c91c71382d59f316181816ae9a2bb9e232a1fcf7cc40000afe69527553f25c996d3bd3ab0392be40c1baaafbb83ed8b1488207213206" + ], + "key_log": null, + "chains": [ + [ + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e16d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010400026e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e7417b98c0c1a0e637327a2d6d5f5fd46e63bb2a5970f6c67586cd7de6f575b6360f92c58afd19b1590b027705e137e7fd9113ec862f4a46f7cf98af22cddf15f6ab0a3d797c19ac7bac710280be04e85315ebb15d4a498b376ba1cf4fa5ef760d0a", + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e16d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc40100000000000000e30951a58b352a71167f2653d553a499f42535929d2c70a2df6707d9237813e9000000000000000000104000332dd253be02fa4f9d861626e9e09ad19786534665f684b833f5beaa94075f5469e9ac354d85ed0e33615f6021464551a549219747f1887e8bd0ba8e198a417289544d1a426e53d808c6e39e490a36f4d7bbbb293038f456841cfe524fb294695727a8c1e1261c0274f4268369b32026b31ae73bfadb3f5c1fdc72341ac65604", + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e16d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc40200000000000000f01481d18e4abd33b4feb973fad90407428633775ee248a3ee1ba5804452b24740420f00000000000012240026e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e741000100010011eec15a7256a01e340ad71d4ca262cbbd3b5d14b89767610b584fe8a879aaf56e8d567369bc64313dc8857a695c44935af619b0ebb63e9a4fbc1d8847130e" + ], + [ + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e1332dd253be02fa4f9d861626e9e09ad19786534665f684b833f5beaa94075f540000000000000000000000000000000000000000000000000000000000000000000000000000000080841e000000000000030800060048656c6c6f3f53e502ef3d4f09c4a615c8374bc433e727bf8bee9a34083ee5ba47ba2bbe463e96959334a02fd78310e063b5ee409626258cbf429e480c30b85f82349c13890a" + ], + [ + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e126e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e74100000000000000000000000000000000000000000000000000000000000000000000000000000000c0c62d000000000000034600230048656c6c6f2e204920616d20616e206175746f6d6174656420617373697374616e742e02857f14d62ade478f90d495a17495e1aa6608252205fe1b4e0bd43dfcf1456761290bea86e3e37ec17ddaa56e7eb3021ac46aca24e23452fb610549bb3599587822fbc702af39ca05d47e519f6a5bf1aecb37e28b2c784abce201d54026a70105", + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e126e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e741010000000000000052c8b1b099988b7700357fdc8c43b4fb67a8174eab58d664b5490ae5a29ec70200093d0000000000000a3000d007000002857f14d62ade478f90d495a17495e1aa6608252205fe1b4e0bd43dfcf145676102656e060048656c6c6f2ebca90ce1ee985ff5ab8d0c6d4e88fed39c76116454f6ce251b82f9b311cff3eb4440ffd246a25255b26a36529eeae05baab1f489ec4f22cc2cce45ce3bc6d306" + ] + ] + }, + "outcome": "refuse", + "reason": "ai_track_unlabelled", + "at": [ + "room", + "record", + 0, + 2 + ] + }, + { + "name": "ai_unmarked", + "about": "an AI participant's chat that states no origin: the room refuses it (live-rooms §9.2 step 6)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"audio\",\"models\":[{\"digest\":\"857f14d62ade478f90d495a17495e1aa6608252205fe1b4e0bd43dfcf1456761\",\"type\":\"trained\"}],\"operation\":\"speech-synthesis\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"7e7b5b7821bc7928d1e8ae3ae2e25b2149ed3a624a6217db003658b9890afd9373c61d7d4d68e8dceb3664ee077006397b95ee3d2e0171caf73bc66bd6edfc09\",\"signer_pubkey\":\"26e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e741\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"from_group\":0,\"track\":\"e0f5f40db551bfb20f862d5d6ffd64dbea588d2181d8dfe58849b4d9f15931a6\",\"type\":\"track\"}}" + ], + "markings": [], + "detections": [], + "reviews": [], + "label_policy": null, + "player": [], + "objects": [], + "decoded": [], + "room": { + "descriptor": "5752443101ba07665226a6cc29262167d963816c0949855436567469a282299e19951d15d86d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc400406cd8e95d060040420f00000000000000080000000200000000005745503101fe1f0000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f00000000000204686f73740101000008040000000000000000000000000000000000000000000000000000000000000861756469656e63650001000018000000000000000000000000000000000000000000000000000000000000000001067370656563680101046f707573000001000000000000100000004000000080bb0000010f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008951f673cfa31eaf7f5b68c7755826574bdf858e85925f8ab4ae2122db97b97ce4f0359e91ccefc8caec38d6ce9b2e90d070141e68d23503d93ba41a40c6b904", + "grants": [ + "5752473101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e126e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e74100000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000001010000000000000000000000000000000000000000000000000000000000000000000000574550310108040000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff0000000100011568747470733a2f2f72656c61792e6578616d706c65450002616940003835376631346436326164653437386639306434393561313734393565316161363630383235323230356665316234653062643433646663663134353637363173f388e6e8845b7c383256ed852a5d354251fac217b9aa5cf1f2021719c1dede958544a127652754264fdf2379ff4dabeb0afe0862afc9674b314239a7ccc10a", + "5752473101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e1332dd253be02fa4f9d861626e9e09ad19786534665f684b833f5beaa94075f5400000000000000000000000000000000000000000000000000000000000000000000000000000000f05ad16a0000000080bad26a000000000100010000000000000000000000000000000000000000000000000000000000000000000000574550310118000000000000000000000000000000000000000000000000000000000000000004f4013200000040420f00000000000100000040420f0000000000ffffffffffffffffffffffffffffffff00000000011568747470733a2f2f72656c61792e6578616d706c6500001903d5c5c1f8f10ba284c91c71382d59f316181816ae9a2bb9e232a1fcf7cc40000afe69527553f25c996d3bd3ab0392be40c1baaafbb83ed8b1488207213206" + ], + "key_log": null, + "chains": [ + [ + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e16d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010400026e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e7417b98c0c1a0e637327a2d6d5f5fd46e63bb2a5970f6c67586cd7de6f575b6360f92c58afd19b1590b027705e137e7fd9113ec862f4a46f7cf98af22cddf15f6ab0a3d797c19ac7bac710280be04e85315ebb15d4a498b376ba1cf4fa5ef760d0a", + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e16d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc40100000000000000e30951a58b352a71167f2653d553a499f42535929d2c70a2df6707d9237813e9000000000000000000104000332dd253be02fa4f9d861626e9e09ad19786534665f684b833f5beaa94075f5469e9ac354d85ed0e33615f6021464551a549219747f1887e8bd0ba8e198a417289544d1a426e53d808c6e39e490a36f4d7bbbb293038f456841cfe524fb294695727a8c1e1261c0274f4268369b32026b31ae73bfadb3f5c1fdc72341ac65604", + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e16d1383a5809797241ba62c20bb2610b653449442b011405f246603c702124cc40200000000000000f01481d18e4abd33b4feb973fad90407428633775ee248a3ee1ba5804452b24740420f00000000000012240026e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e741000100010011eec15a7256a01e340ad71d4ca262cbbd3b5d14b89767610b584fe8a879aaf56e8d567369bc64313dc8857a695c44935af619b0ebb63e9a4fbc1d8847130e" + ], + [ + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e1332dd253be02fa4f9d861626e9e09ad19786534665f684b833f5beaa94075f540000000000000000000000000000000000000000000000000000000000000000000000000000000080841e000000000000030800060048656c6c6f3f53e502ef3d4f09c4a615c8374bc433e727bf8bee9a34083ee5ba47ba2bbe463e96959334a02fd78310e063b5ee409626258cbf429e480c30b85f82349c13890a" + ], + [ + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e126e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e74100000000000000000000000000000000000000000000000000000000000000000000000000000000c0c62d000000000000032500230048656c6c6f2e204920616d20616e206175746f6d6174656420617373697374616e742eed26431d6673851b421ceaa718d614e7dd21643d1a82a6d053883e1e7ae722e0440783d5a2e151115fa23b8a8ae0de570297987d47e192438b2d7c9028900d08", + "5745563101e36c341a23bf13a3594cc864696fb9a89f85cba60faaa0ddf4e776aee2b976e126e40ba726b5d0bd3cba619ecd280967a9f28dff54c01f2c6bc27cd9e0e1e74101000000000000006aec06d5a53a79a3b40ed7be1551ecedcc4aff4ce8eb700b6504bbaca2ad837d00093d0000000000000a3000d007000002857f14d62ade478f90d495a17495e1aa6608252205fe1b4e0bd43dfcf145676102656e060048656c6c6f2e539273e572c829de9e25ec2fa5503cd0240d3e54671d52215fe86fc6aa751c2a2f3aee6f09613ba79e72976b2bbb942b118a1481e689094a7ebb5f21bf59c205" + ] + ] + }, + "outcome": "refuse", + "reason": "ai_unmarked", + "at": [ + "room", + "record", + 2, + 0 + ] + }, + { + "name": "player_chain_broken", + "about": "a player's log whose third entry claims seq 3 (§10.3)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": null, + "player": [ + "{\"at\":0,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"first-exposure\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"1071b9e5bc259e948b29c4b6e79c76a4749000d0998f97bb218160faac47a925d8df36ed6fe62a3586c8adae3c3ba32dbc21ad1434bed9c3c00c424b1962950f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"d43d753b42fa00b30b4bde2820034298f165e27cf9b1b1b8c78e99131e1c8d8a\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"fe343689ded585d0cb6c9b78e508152dd3f537125c2634bdc0c53b53402d7db14af0aac5431c8a24059d5f005d8d501f1cd68d1ff2bfa62fde8858a945506f01\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":1000000,\"entry\":{\"review\":\"f53388638eec4d3c50ad26873a266d51a5468f2b9df922e30d93e3980fa60282\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"review\"},\"kind\":\"wai-player\",\"prev\":\"780d0b5b9cb2a9439625ed2ae4cd88fa58078cd8faf9a93ce698f415467a79a3\",\"seq\":3,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"138a9392b83fb6ee462d2ef1da462ebbd86c239e50aa9ceaeca943aaf91aee47cc6a8acb9829cf154b77502351dc6e4e41e4debda6c56d3712b2e41e8155e406\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20000000,\"entry\":{\"after\":\"ad-break\",\"type\":\"resume\"},\"kind\":\"wai-player\",\"prev\":\"8798772ab497744055c606abf9512a977710114b1d2e823fe8feb41e87ff94b1\",\"seq\":3,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"07605ee164d8b229cc90d97cee85ae4f16024b2ebd8d4ed36a1b2cd71f3c6b0dbbed9c2bbe2aecd99047c0f9bce48dbcf22fab06121382c3bfbd750400b89b09\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"resume\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":\"e74865cf7556384fb5f978b1c89baa512762d79a4dda6e5fd3de460f211a46be\",\"seq\":4,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"082fb36c2f63a351bd2e6a5a1d697f300cda5a33405d08bf42a9d79f2c1e2579735d4ab1dd46cb1e2d51c25ab601135117df90ebdc34d61516cbcabf4798d802\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"0ce999210725d04111c829732c363e5587c1a13eb6990a74bf6ab09b3e4faa8f\",\"seq\":5,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"66ba5826145a6d7a17ed89e8bf8faa725bb34942afb3e86046a4d7380c57f8e7fec3bb6d3e55d01e76927065f13536b86a061eec0ba776de732462e61ed6940d\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":40000000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"4c772443e1c5b98c79ff193646ec9ee9ef00062f0bb8ab89330d08088e281686\",\"seq\":6,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"4429d3e146c5f24ad739037848fe8531a5978b062632351c231b5a827e9463b4390143db6d7a4acfe9b2f0234dd4826baa60930422ab2af65746d34716ca1a0f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "player_chain_broken", + "at": [ + "player", + 2 + ] + }, + { + "name": "present_mismatch", + "about": "a presentation that states the class assistive for a synthesized object (§10.3)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": null, + "player": [ + "{\"at\":0,\"entry\":{\"class\":\"assistive\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"1ea1852b12e1a16e3d00536ee2f72f55aaf4fe3e22502382580417551ce70132c7b1885ca9de36147547cac754b446418dedb248803944916d1a3b21db8ad106\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "present_mismatch", + "at": [ + "player", + 0 + ] + }, + { + "name": "present_output_irreproducible", + "about": "a presentation whose stated output is not what the object decodes to (§9.2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": null, + "player": [ + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"3b5fba5eb3db540dac0c9a53ed9b9d5baea76983b6c0ea792ec245123afea97f\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"82460c3e5fc6dc63ba05f2a9304396a6086e0d15f08e05f73f35186f9ce6023ce602506fd22935e7fa281a0f7339620959445100879fd27ef271387c91874f09\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "present_output_irreproducible", + "at": [ + "player", + 0 + ] + }, + { + "name": "review_subject_mismatch", + "about": "a review presented for an object the player has not presented (§9.2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}", + "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": null, + "player": [ + "{\"at\":0,\"entry\":{\"class\":\"captured\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"image\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"output\":null,\"subject\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"4db091b756ed0adce9280a4a98e83ac6c7c03e593d5c30100753b4cbcc8c51ad833952bbf241b826a10a00161b160cb9f7f0590377afd87eaf3c1b7c5bb52b09\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":1000000,\"entry\":{\"review\":\"f53388638eec4d3c50ad26873a266d51a5468f2b9df922e30d93e3980fa60282\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"review\"},\"kind\":\"wai-player\",\"prev\":\"f08067bc424759141b9707e3c29a0244f0cf979fb3e4287662d8bce4254007cc\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"8546cd13e3241c2f05e6b86a0c1d428c131fe92aae4c510ccddb60fc069416478d58672311f4ddebb05e30a690b0b44a4b33bf2382c60d2e4deed6ba60b51e06\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [], + "decoded": [], + "room": null, + "outcome": "refuse", + "reason": "review_subject_mismatch", + "at": [ + "player", + 1 + ] + }, + { + "name": "review_rejected", + "about": "a rejected review presented as a review (§9.2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":\"2289937744cfc8400150b9fc1e54ff8483a8cc133dbefeb7c8585abba617c656\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"64533ec6c26fa5d923e12a5ec3821ac2c7a2ebb0285ada93726fa61f8998fe0e0743428eeeca0e3dc00eb1518094da9303c2210265c19b504d13713c58141a0d\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"rejected\"}" + ], + "label_policy": null, + "player": [ + "{\"at\":0,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"first-exposure\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"1071b9e5bc259e948b29c4b6e79c76a4749000d0998f97bb218160faac47a925d8df36ed6fe62a3586c8adae3c3ba32dbc21ad1434bed9c3c00c424b1962950f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"d43d753b42fa00b30b4bde2820034298f165e27cf9b1b1b8c78e99131e1c8d8a\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"fe343689ded585d0cb6c9b78e508152dd3f537125c2634bdc0c53b53402d7db14af0aac5431c8a24059d5f005d8d501f1cd68d1ff2bfa62fde8858a945506f01\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":1000000,\"entry\":{\"review\":\"caf32843208613369c28061e2359d8d32c21b3dbb35a0002bcc23e4aecde4976\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"review\"},\"kind\":\"wai-player\",\"prev\":\"780d0b5b9cb2a9439625ed2ae4cd88fa58078cd8faf9a93ce698f415467a79a3\",\"seq\":2,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"49037448b7380753ee0eac6872f296d3b7c117d726c9dbb6794ed9e6f1ba375a77a2eba06aca78ec1c7f308899430da6ab856a7da272e5736125cc350a134d03\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20000000,\"entry\":{\"after\":\"ad-break\",\"type\":\"resume\"},\"kind\":\"wai-player\",\"prev\":\"836d89d5b7c71735f05d384c7b3601f83c108dc197de88efe1b0814d2a82b2b8\",\"seq\":3,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"b8d7e997906e501b27f9c57f55eac8adddae05ef116537b29c67f52bb34ad8088e93156a90707b0f7630f064f71f2fa3f7f358c80b388658ed64f7fb4edd8504\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"resume\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":\"9ffd5c79482c4bd2628191f4007cc6bba08b50f9e6d5f2e53e57732c26f58331\",\"seq\":4,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"96b21455f13f9fd901b684c6b1e458d3f6b545219d768340b1b14ec2cbb2538aed32e55f1f224d07d9506705d200e9e85a191da9dc3d7dda85a63ccc6e10f008\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"433c3da0885e5baab9892de4af5213ae8168b23b9f6f5e115f046e7543f01a3c\",\"seq\":5,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"7b55ff96c4cfbe269ab443e371c2e2b32cad19679066cc2723cb2dc304ec6c162323d74be5aafadcff31aa94925458580ce98aecb5fc30a1598d831b525c5a0b\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":40000000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"3e41d12949a681e2f73bd37149308f46df40f01e38da3edd2a00eab0e21b7532\",\"seq\":6,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"d4b024dde23095aec2e9655c7fafdb5519cc8e3ab1e42f856bf84eacc70538bc94f80e8ca692ce4c6d4420fb9d448901ae0a6cb816ec5eac6628373c7301ad01\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "review_rejected", + "at": [ + "player", + 2 + ] + }, + { + "name": "review_path_not_exact", + "about": "a review presented on a non-exact path: the player decoded through a float path, tier none, and presents a review made at decode-equivalence (§9.2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": null, + "player": [ + "{\"at\":0,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"first-exposure\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"1071b9e5bc259e948b29c4b6e79c76a4749000d0998f97bb218160faac47a925d8df36ed6fe62a3586c8adae3c3ba32dbc21ad1434bed9c3c00c424b1962950f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"none\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"d43d753b42fa00b30b4bde2820034298f165e27cf9b1b1b8c78e99131e1c8d8a\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"a0f9c824a92d705550daa996995cd11fed4a3726d32423561f035a61b0e52cd5b1d392943bed269a619c8a723a50b508b8e66882cf3ffb25d18e5fc373de5b0c\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":1000000,\"entry\":{\"review\":\"f53388638eec4d3c50ad26873a266d51a5468f2b9df922e30d93e3980fa60282\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"review\"},\"kind\":\"wai-player\",\"prev\":\"42cd88a2a3ae83a7d6274583661a01ca6e232b18c5d00cf32c062e25c08066fb\",\"seq\":2,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"8c27e4ad655a0424adf11c8e3f5a0143d78ee96240744a7cfa3d70109abb03d26ca3500bb59239c8a0763cac6ee4e5984aa02a306c0458b9071c0835e087c30a\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20000000,\"entry\":{\"after\":\"ad-break\",\"type\":\"resume\"},\"kind\":\"wai-player\",\"prev\":\"c4cf7acaab920e31e97e905ce5e7a4728379fcd45f941c7b2b419c937406bfb7\",\"seq\":3,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"40beb6601e0937f48eba68205f32c63a55b863cf727a3ac9ae42a46c5079edeb53348addb9b52b94b22eb8596478f9618de753c56fe706dde6ccfa731213a606\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"resume\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":\"59dfd3011bc41eac69093c898f1741cd961cff83a187d1ebee92390ee43999ba\",\"seq\":4,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"b558dfd15d3ead0f31a89d31f6786055709921a1153a7ec67255b9ec2fa6afcd03e774f02c31ee9dbfe73ea1793eed1355c97ed8a0be549ea1120c6d09c7a208\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"none\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"b7e80bf47b7284974e1c981f275f4b3e09380762dd4e5b66945514a80a1cfcc5\",\"seq\":5,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"a01507cd82477d0ed71f10f503882d2f292dec099a794c6415d17b05c10669a15b6da91fce1e3f3d2ec822f261bef95d536675392f8a2b331bcee48ef37f630b\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":40000000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"none\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"3b8adf722ceebe95bc880e727d85c3df6961afc32444f4597bb9f4cfcd03d46e\",\"seq\":6,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"6f0b6f701704e0db869f971a7ae2d2ced80043e48cded58e60cca80390604bf093b1b0efaab8136a840503911da46b8e0a662a0233cb1633e40b35a613a41807\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "review_path_not_exact", + "at": [ + "player", + 2 + ] + }, + { + "name": "review_reconstruction_mismatch", + "about": "a review over another reconstruction than the one the player presented (§9.2)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"ba04f104eaca15e623d77e6f76a4d6f25bf34ba874689075f2f4355acba117b9\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"776c309c7f12edcd1ecb648a64ea6ea925c7bc98380b3977894a20fbccaadc1ed10c13ba78ef193cc64d3aeb3b957bbd4a7723971f213fc1b6d35dbbe7ac9602\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": null, + "player": [ + "{\"at\":0,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"first-exposure\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"1071b9e5bc259e948b29c4b6e79c76a4749000d0998f97bb218160faac47a925d8df36ed6fe62a3586c8adae3c3ba32dbc21ad1434bed9c3c00c424b1962950f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"d43d753b42fa00b30b4bde2820034298f165e27cf9b1b1b8c78e99131e1c8d8a\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"fe343689ded585d0cb6c9b78e508152dd3f537125c2634bdc0c53b53402d7db14af0aac5431c8a24059d5f005d8d501f1cd68d1ff2bfa62fde8858a945506f01\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":1000000,\"entry\":{\"review\":\"f75c31a84eed9a61e63ef9fc0c755d55ab4e4a484e1a15ad2cc784598c8e7d9a\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"review\"},\"kind\":\"wai-player\",\"prev\":\"780d0b5b9cb2a9439625ed2ae4cd88fa58078cd8faf9a93ce698f415467a79a3\",\"seq\":2,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"6b1fdf461b3ad2b164bbdebbc4c651d93f5993d3a02da5f261ec809ac0eab74626725f865700a7d00a48419cf6ead1962ea428b3564f5041992625c4bd351f06\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20000000,\"entry\":{\"after\":\"ad-break\",\"type\":\"resume\"},\"kind\":\"wai-player\",\"prev\":\"6d050a1d6957f9e5130b9ab42b117ae41730a8ee11132077fb95dc6f638dafc9\",\"seq\":3,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"550c2359ee27b9f77659a9a46ed1dfa48d8cc2cae6c6579c9ea305f5a76ff828c6e205ac625f949b846ab2a1bbf3fc49ea0e25e02bd3f1832c591e99e9b7570b\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"resume\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":\"66a619e4026deab8f013042576712be47facdd1dae417d8e939c5d2b3f362458\",\"seq\":4,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"8c54f3f3dde8c3cc417cbd72b57951939a731773397c70a92a4d0eb722260dd541dca554f8b4778ab3eb9c3ab8e72dd7b10e45254c7fa88f4a0a33f33e0a3a02\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20200000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"cab9bdbdd7830643a070d2282ce445b01415ed63418a077862cf59e869b6dbd6\",\"seq\":5,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"65303588a64ee603c98d665ac2322188453013bc34ad662c7a6a3866cd9356049e7dc2822c3570a8fe950008cab92910d7096c3829fd0ec390a38b37b4293d00\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":40000000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"ed8724dd2a9baa3d91d4337ca476edc5eab15c9905965c857e4a8b64e8da2852\",\"seq\":6,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"ef9e7426734ba4d8427648ebf86c3f1d5d1ba1b69ebf29fd18ba86c917558db0f86a4a128c54819bf145e68b90b05719c65cbba4a371dcc5025ff7cff58cc000\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "review_reconstruction_mismatch", + "at": [ + "player", + 2 + ] + }, + { + "name": "label_asset_mismatch", + "about": "a label shown with another icon than the policy's (§10.4)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": "{\"audio\":\"6e0d3d5e9f9661b8c2327e57ebe1b360b204e109def81020a485edb8bf2f58e8\",\"classes\":[\"synthesized\"],\"grace_us\":500000,\"interval_us\":null,\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-label-policy\",\"sig\":\"b61490b72f87faabcfd2e5d34b3ec3093b0d7e5378657eab2d8a24eec7238bf846ac7093e9fc25ab85a8544ac0247b9d672c71e05823f14afa1c58b794ef2702\",\"signer_pubkey\":\"34e2cdfcf44eaae9f3974cd801473ae99e6779b7e3d432fc12ff35f26d429948\",\"visual\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\"}", + "player": [ + "{\"at\":0,\"entry\":{\"asset\":\"2e7349e5eca4ee78eeef160dfef4545c31850932245373a07c5401e73e6599c0\",\"label\":\"visual\",\"trigger\":\"first-exposure\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"ca56cbca830539174672ec41f3211e18e5c52dc7b9790bd1a379bf0032b3ae5b0b7c72f459a3c550bb5f9d4d38eeb20a1ce2c9a1392bd1f79657bc510ad5fe0e\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"765545f6441d7daec49eb3dbbd918721bf7de95db6cf6fca2733f49b7f0e4690\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"88c3b89e4afedca57ab3855fed7cd06d11b8cb99eb028d6f5270ede51514209bdfdb405b8c8b47afb346618cdd20c90f1bb5df9cf3f7827a583da1ac404b1f02\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "label_asset_mismatch", + "at": [ + "player", + 0 + ] + }, + { + "name": "label_not_shown", + "about": "a label not shown at first exposure: the player's log shows the generated video presented at 0 s and the label first at 5 s, past the half-second grace (§10.4)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": "{\"audio\":\"6e0d3d5e9f9661b8c2327e57ebe1b360b204e109def81020a485edb8bf2f58e8\",\"classes\":[\"synthesized\"],\"grace_us\":500000,\"interval_us\":null,\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-label-policy\",\"sig\":\"b61490b72f87faabcfd2e5d34b3ec3093b0d7e5378657eab2d8a24eec7238bf846ac7093e9fc25ab85a8544ac0247b9d672c71e05823f14afa1c58b794ef2702\",\"signer_pubkey\":\"34e2cdfcf44eaae9f3974cd801473ae99e6779b7e3d432fc12ff35f26d429948\",\"visual\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\"}", + "player": [ + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"f21bba0e5692e924276851e11a2b25c5c6f89a04bd2203ee6b3ad9bb05fe7730a719f898349e508cac73cb0dfa00e92d448a332d7089e91b8f930ac7dcfd740f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":5000000,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"first-exposure\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":\"fb96c762c013d781cea7e1a4b0039d4e9d08589e598dbd495a7b97c2cb42391f\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"cd1c449189286f52e170c01a3a140d62e88874976fbcba205d8fc889ec1971de3a12cf1ad62330fcd73e135dd1c736def497d3c9ab5feb832c31a2feb3f6af0e\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "label_not_shown", + "at": [ + "player", + 0 + ] + }, + { + "name": "label_not_shown_resume", + "about": "no label after an ad break: presentation resumes at 20 s with no label shown since (§10.4)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": "{\"audio\":\"6e0d3d5e9f9661b8c2327e57ebe1b360b204e109def81020a485edb8bf2f58e8\",\"classes\":[\"synthesized\"],\"grace_us\":500000,\"interval_us\":null,\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-label-policy\",\"sig\":\"b61490b72f87faabcfd2e5d34b3ec3093b0d7e5378657eab2d8a24eec7238bf846ac7093e9fc25ab85a8544ac0247b9d672c71e05823f14afa1c58b794ef2702\",\"signer_pubkey\":\"34e2cdfcf44eaae9f3974cd801473ae99e6779b7e3d432fc12ff35f26d429948\",\"visual\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\"}", + "player": [ + "{\"at\":0,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"first-exposure\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"1071b9e5bc259e948b29c4b6e79c76a4749000d0998f97bb218160faac47a925d8df36ed6fe62a3586c8adae3c3ba32dbc21ad1434bed9c3c00c424b1962950f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"d43d753b42fa00b30b4bde2820034298f165e27cf9b1b1b8c78e99131e1c8d8a\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"fe343689ded585d0cb6c9b78e508152dd3f537125c2634bdc0c53b53402d7db14af0aac5431c8a24059d5f005d8d501f1cd68d1ff2bfa62fde8858a945506f01\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20000000,\"entry\":{\"after\":\"ad-break\",\"type\":\"resume\"},\"kind\":\"wai-player\",\"prev\":\"780d0b5b9cb2a9439625ed2ae4cd88fa58078cd8faf9a93ce698f415467a79a3\",\"seq\":2,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"6079f3cea97c7c11afa22040ff77834432ba3b4cee60d3151005e76eec1fdedbfb3ece516967912b70157b11509bb6e70f786b0be2427e037369cf423cc52002\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":20000000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"a2577eca3523191dabfefae089c7d147d27dbccbda1154097d4b7afa6ba7358e\",\"seq\":3,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"f9cea1860d28fdaf58c436674c7f84b70ebb19b41dd3a3151fdcab8a48cdbd0834205cea6d541aecff08225cb92f179f59df8468af84d7360a52cf90e2bc710c\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "label_not_shown", + "at": [ + "player", + 3 + ] + }, + { + "name": "label_not_shown_interval", + "about": "no label within the policy's 30 s interval: the label last shown at 0 s, the video still presented at 45 s (§10.4)", + "policy": { + "dual_layer": "when-given", + "assistive": "accept-asserted" + }, + "claims": [ + "{\"class\":\"synthesized\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"video\",\"models\":[{\"digest\":\"1f6d80520cd377fd820fa6727c88d5ef63bb3d4e88592c4915198d92f9d8fec0\",\"type\":\"trained\"}],\"operation\":\"video-generation\",\"oversight\":\"fully_autonomous\",\"record\":null,\"sig\":\"ca9048fd3de7f91e9d7942ca66b2416a447448a37c8008e883aba0ea8b12612cb54c404d1ef63e613d10cfe8753712420756e121e0567790d26cc93bd616d90a\",\"signer_pubkey\":\"05e74a13f7535dd2908c8fdd5b5b0fe6e28b49364593a60aca4461fda5c335ca\",\"source\":\"trainedAlgorithmicMedia\",\"step\":\"generate\",\"subject\":{\"content_hash\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"type\":\"object\"}}" + ], + "markings": [], + "detections": [], + "reviews": [ + "{\"decoder\":\"builtin:wai@0.3.0\",\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-review\",\"notes\":null,\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"reconstruction\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"role\":null,\"scope\":\"rendering\",\"sig\":\"a83e554b18fb64e19b394adbd5c866b30ee79de5583e1a083b1187c6c30aa42396ca1a67e755e6cb7f45ce307caa2d9d054903d2c71c59731b32b46dc3629b0b\",\"signer_pubkey\":\"50999f876122d351b1a229a604ec946ddd24838b9e10d2468259405fe4695220\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"variant\":null,\"verdict\":\"approved\"}" + ], + "label_policy": "{\"audio\":\"6e0d3d5e9f9661b8c2327e57ebe1b360b204e109def81020a485edb8bf2f58e8\",\"classes\":[\"synthesized\"],\"grace_us\":500000,\"interval_us\":30000000,\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-label-policy\",\"sig\":\"50693722b2490992604330c305b695ac8ce99b2e4ac291dadbedcc9a0d9e128f965ee347a360675481d441f67145b5dd22417345274b9b337953ebb23479d300\",\"signer_pubkey\":\"34e2cdfcf44eaae9f3974cd801473ae99e6779b7e3d432fc12ff35f26d429948\",\"visual\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\"}", + "player": [ + "{\"at\":0,\"entry\":{\"asset\":\"503af176b05fd725e68b0aa526977d31bcd657d74b15c6103a57ace81384940f\",\"label\":\"visual\",\"trigger\":\"first-exposure\",\"type\":\"shown\"},\"kind\":\"wai-player\",\"prev\":null,\"seq\":0,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"1071b9e5bc259e948b29c4b6e79c76a4749000d0998f97bb218160faac47a925d8df36ed6fe62a3586c8adae3c3ba32dbc21ad1434bed9c3c00c424b1962950f\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":0,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"d43d753b42fa00b30b4bde2820034298f165e27cf9b1b1b8c78e99131e1c8d8a\",\"seq\":1,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"fe343689ded585d0cb6c9b78e508152dd3f537125c2634bdc0c53b53402d7db14af0aac5431c8a24059d5f005d8d501f1cd68d1ff2bfa62fde8858a945506f01\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}", + "{\"at\":45000000,\"entry\":{\"class\":\"synthesized\",\"decoder\":\"builtin:wai@0.3.0\",\"media\":\"video\",\"origin\":\"ead74abe77e32490a3e184e17b4a7e8296c102dba77118b02fbebd20b75cd3f0\",\"output\":\"5ac2b64f4b8208dda9cd224d92353fd74b917d3ada6c08c65dfe95712ce32ef6\",\"subject\":\"66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5\",\"tier\":\"decode-equivalence\",\"type\":\"present\",\"variant\":null},\"kind\":\"wai-player\",\"prev\":\"780d0b5b9cb2a9439625ed2ae4cd88fa58078cd8faf9a93ce698f415467a79a3\",\"seq\":2,\"session\":\"05592ceaa80207a5dd1d9e8e7d5c99498f383eccd17c771a52ecc0a3f5f0f7cd\",\"sig\":\"cb4d6410218850372793539a29ea2b0a311ef813d6284f9fc172e4dba10b94c7bb95e3397cf491ffe4daa4845dd634d6cad53072dd24cc43dd8dcd043666da00\",\"signer_pubkey\":\"008980373ce127480e76dfdc937a2c8f61014c328d905c99c47becbd89ba1c5f\"}" + ], + "objects": [ + "57414931660000007b22776169223a22312e30222c226d65646961223a22766964656f222c22696e74656e74223a227065726365707475616c222c226d6f64656c5f726571756972656d656e74223a7b226361706162696c697479223a227761692e766964656f2e617631227d7d400000001c8af660db156fd4777ceafd936d90bf46e4ea9d79272f80bbcfdce23a6da360aa0772307063ca56ee4d1d2b75d5de565c3895a9c230ba3facfad6d1579caf53" + ], + "decoded": [ + { + "object": "66aef0dce547f18873c4d53fdbf1c23231f0ea7f438be6e340037bda899073f5", + "samples": "c5ab2471427ff3fc018ffd5106fede9fa4155895fffa97099b03775d4b1a277cfb628b04" + } + ], + "room": null, + "outcome": "refuse", + "reason": "label_not_shown", + "at": [ + "player", + 2 + ] + } + ] +} diff --git a/wai/wai-rs/Cargo.toml b/wai/wai-rs/Cargo.toml index 2f54cb110..34ca6d4da 100644 --- a/wai/wai-rs/Cargo.toml +++ b/wai/wai-rs/Cargo.toml @@ -255,6 +255,15 @@ live_watermark = ["live_room"] # wai.video.ssai's receipt: the decision recomputed, the asset list bound, the spend held to the # decider grant's ceiling. SHA-256, BLAKE3, Ed25519 and the key log only; no new dependency. live_ads = ["live_room", "video_ssai"] +# Synthesis provenance (extensions/synthesis): `wai.media.synthesis` — the origin claim of an +# object, a group or a track (captured, reconstructed, assistive or synthesized; the class rises +# along a chain of derivations and never falls), its IPTC digital source type and C2PA statement, +# the two pinned integer derivations whose recomputation proves an assistive claim, the marking +# and detection records of the second marking layer and their agreement with the claim, the +# tracks of a room's AI participants, reviews that a player presents only on a decode path of +# the review's tier, and the label obligations of a player's signed log. SHA-256, BLAKE3, +# Ed25519 and the key log only; no new dependency. +synthesis = ["live_room"] # Timed text and live captions (extensions/timed-text): `wai.text.timed` — the cue model, # live caption revisions (`caption` events of wai.live.events with the WTX1 annotation), # and their exact mappings onto WebVTT documents and segments, the IMSC1 Text Profile, @@ -709,6 +718,11 @@ name = "wai_live_moderation_vectors" path = "src/bin/wai_live_moderation_vectors.rs" required-features = ["live_moderation"] +[[bin]] +name = "wai_synthesis_vectors" +path = "src/bin/wai_synthesis_vectors.rs" +required-features = ["synthesis"] + [[bin]] name = "wai_live_watermark_vectors" path = "src/bin/wai_live_watermark_vectors.rs" @@ -1400,6 +1414,11 @@ name = "live_moderation" path = "tests/live_moderation.rs" required-features = ["live_moderation"] +[[test]] +name = "synthesis" +path = "tests/synthesis.rs" +required-features = ["synthesis"] + [[test]] name = "live_watermark" path = "tests/live_watermark.rs" diff --git a/wai/wai-rs/src/bin/wai_synthesis_vectors.rs b/wai/wai-rs/src/bin/wai_synthesis_vectors.rs new file mode 100644 index 000000000..c5b4f06bb --- /dev/null +++ b/wai/wai-rs/src/bin/wai_synthesis_vectors.rs @@ -0,0 +1,74 @@ +//! Conformance corpus for synthesis provenance (`live-conformance/synthesis/`, extensions/synthesis.md §13). +//! +//! wai_synthesis_vectors gen write vectors.json from fixed seeds +//! wai_synthesis_vectors verify every file regenerates byte for byte, no other +//! file but README.md is there, and every case +//! gets the outcome it states +//! +//! The generator and this verifier are one implementation. The corpus's second +//! implementation is `live-conformance/synthesis_verify.py`, written from the extension's +//! text and sharing no code with this binary. + +use std::collections::BTreeMap; +use std::path::Path; +use wai::synthesis::corpus; + +fn main() { + let a: Vec = std::env::args().collect(); + match (a.get(1).map(String::as_str), a.get(2)) { + (Some("gen"), Some(d)) => { + if let Err(e) = std::fs::create_dir_all(d) { + eprintln!("create {d}: {e}"); + std::process::exit(1); + } + for (name, body) in corpus::generate() { + let p = Path::new(d).join(&name); + if let Err(e) = std::fs::write(&p, body) { + eprintln!("write {}: {e}", p.display()); + std::process::exit(1); + } + } + println!("wrote {d}"); + } + (Some("verify"), Some(d)) => std::process::exit(verify(Path::new(d))), + _ => { + eprintln!("usage: wai_synthesis_vectors gen|verify "); + std::process::exit(2); + } + } +} + +fn verify(dir: &Path) -> i32 { + let mut files = BTreeMap::new(); + let entries = match std::fs::read_dir(dir) { + Ok(e) => e, + Err(e) => { + eprintln!("read {}: {e}", dir.display()); + return 1; + } + }; + for e in entries.flatten() { + let name = e.file_name().to_string_lossy().into_owned(); + if name == "README.md" { + continue; + } + match std::fs::read_to_string(e.path()) { + Ok(s) => { + files.insert(name, s); + } + Err(err) => { + eprintln!("read {name}: {err}"); + return 1; + } + } + } + let generated: BTreeMap = corpus::generate().into_iter().collect(); + let same = files == generated; + println!("{} every file regenerates byte for byte from its seeds, and no other file is there", if same { "PASS" } else { "FAIL" }); + let fails = corpus::verify(&files); + for f in &fails { + println!("FAIL {f}"); + } + println!("{} every case gets the outcome the corpus states", if fails.is_empty() { "PASS" } else { "FAIL" }); + (!same || !fails.is_empty()) as i32 +} diff --git a/wai/wai-rs/src/lib.rs b/wai/wai-rs/src/lib.rs index 189e83f18..e6086d434 100644 --- a/wai/wai-rs/src/lib.rs +++ b/wai/wai-rs/src/lib.rs @@ -460,6 +460,15 @@ pub mod energy_steering; /// cbindgen:ignore #[cfg(feature = "live_moderation")] pub mod live_moderation; +// Synthesis provenance (extensions/synthesis.md): `wai.media.synthesis` — the origin claim of an +// object, a group or a track and its class (captured, reconstructed, assistive, synthesized), +// its IPTC digital source type and C2PA statement, assistive claims proved by recomputing a pinned +// integer derivation, the two marking layers and their agreement, AI participants' tracks, +// reviews presented only on a decode path of their tier, and a player's label log. Rust-only, +// like the live modules: its constants must not become macros in include/wai.h. +/// cbindgen:ignore +#[cfg(feature = "synthesis")] +pub mod synthesis; // Forensic watermark sessions (extensions/forensic-watermark.md): `wai.live.watermark` — the edge's // session record of the ETSI TS 104 002 A/B variants it embedded, bound to the session's grant by a // salted commitment, the recovered sequence and the rule that matches it, and the room authority's diff --git a/wai/wai-rs/src/synthesis.rs b/wai/wai-rs/src/synthesis.rs new file mode 100644 index 000000000..97fe1938b --- /dev/null +++ b/wai/wai-rs/src/synthesis.rs @@ -0,0 +1,1402 @@ +//! Synthesis provenance (`extensions/synthesis.md`): the record `wai.media.synthesis`. +//! +//! An **origin claim** (`wai-origin`) says of one object, one group or one track what it +//! is: `captured`, `reconstructed` (a codec, neural or not, rebuilding what was captured), +//! `assistive` (an edit that does not substantially alter its input) or `synthesized` +//! (generated). Its class is the greater of the class of the step that made it and the +//! classes of its inputs, each named by the link hash of its own claim, so the class rises +//! along a chain of derivations and never falls (§3). It restates the content's IPTC +//! digital source type, which is a function of the claim (§5), and so is its C2PA statement: +//! the actions and the `c2pa.ai-disclosure` assertions a manifest carries (§6). An assistive +//! claim carries **evidence**: where the step is one of the pinned integer derivations of §4, +//! a verifier that holds the input's decoded samples recomputes the output and compares it, +//! so the claim of assistiveness is checked rather than trusted; any other assistive step can +//! only assert it. +//! +//! Dual-layer marking (§7): the signed claim is the first layer. The second is a watermark, +//! named by a **marking** record (`wai-marking`) whose payload is a function of the claim, +//! and read back by a **detection** record (`wai-detection`); a verifier refuses a detection +//! whose payload disagrees with the claim. This revision registers no watermark algorithm: +//! the scheme is a slot the later slice SYN-2 fills. +//! +//! AI participants of rooms (§8): live-rooms §4.4 and §9.2 step 6 mark them and hold their +//! events to it; this module adds that every track such a participant publishes has a track +//! claim of class `synthesized` naming its model. +//! +//! Certified synthesis (§9): a **review** (`wai-review`) is a signed verdict over one +//! reconstruction at a determinism tier, and a player presents it only for a presentation +//! whose decode path holds that tier and whose output is the reconstruction reviewed. +//! +//! Label display (§10): a **label policy** (`wai-label-policy`) says which classes are +//! labelled and with which asset, and a player's signed **log** (`wai-player`) of what it +//! presented, what label it showed and when presentation resumed after an interruption is +//! held to it: at first exposure, after each interruption, and at the policy's interval. +//! +//! The corpus is `live-conformance/synthesis/` ([`corpus`]); its second implementation is +//! `live-conformance/synthesis_verify.py`. + +use std::collections::{BTreeMap, BTreeSet}; + +use ed25519_dalek::{Signer, SigningKey}; +use serde_json::{json, Map, Value}; +use sha2::{Digest, Sha256}; + +use crate::claim_receipt::{rfc3339, term_ok}; +use crate::jcs::{jcs, MAX_INTEGER}; +use crate::live_events::{Event, Payload}; +use crate::live_room::{self as room, Detail}; + +pub mod corpus; + +/// The domain every record of this extension is signed under (§2). +pub const SIGN_DOMAIN: &[u8] = b"wai:synthesis\x01"; +/// The domain of a record's link hash (§2). +pub const LINK_DOMAIN: &[u8] = b"wai:synthesis-link\x01"; +/// The IPTC Digital Source Type vocabulary, whose codes §5 uses. +pub const IPTC: &str = "http://cv.iptc.org/newscodes/digitalsourcetype/"; +/// The prefix of a model's identifier in an AI disclosure (§6), timed-text §10.3's. +pub const MODEL_ID: &str = "tag:transaction.science,2026:wai/model/"; +/// The pinned integer derivations of §4. +pub const UPSCALE2: &str = "wai-synthesis:upscale2"; +pub const DENOISE3: &str = "wai-synthesis:denoise3"; +/// The most bytes a derivation's samples hold (§4). +pub const MAX_SAMPLES: u64 = 1 << 26; +/// The most inputs and models a claim names (§3.1). +pub const MAX_LIST: usize = 16; + +/// Why a case is refused (§12). +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Reason { + RecordMalformed, + BadKey, + BadSignature, + ClaimConflict, + ClassMismatch, + SourceMismatch, + InputMismatch, + ClassUnderstated, + EvidenceInvalid, + InputSamplesMismatch, + AssistiveRecomputeMismatch, + SamplesMismatch, + AssistiveNotProved, + ClaimUnknown, + MarkingMismatch, + DetectionMismatch, + WatermarkAbsent, + LayersDisagree, + LayerMissing, + AiTrackUnlabelled, + PlayerChainBroken, + PresentMismatch, + PresentOutputIrreproducible, + ReviewSubjectMismatch, + ReviewRejected, + ReviewPathNotExact, + ReviewReconstructionMismatch, + LabelAssetMismatch, + LabelNotShown, + /// A room verifier's code (live-rooms §11, live-events §9), raised at the room. + Room(room::Reason), +} + +impl Reason { + /// The codes this extension adds, in §12's order. + pub const ALL: [Reason; 29] = [ + Reason::RecordMalformed, + Reason::BadKey, + Reason::BadSignature, + Reason::ClaimConflict, + Reason::ClassMismatch, + Reason::SourceMismatch, + Reason::InputMismatch, + Reason::ClassUnderstated, + Reason::EvidenceInvalid, + Reason::InputSamplesMismatch, + Reason::AssistiveRecomputeMismatch, + Reason::SamplesMismatch, + Reason::AssistiveNotProved, + Reason::ClaimUnknown, + Reason::MarkingMismatch, + Reason::DetectionMismatch, + Reason::WatermarkAbsent, + Reason::LayersDisagree, + Reason::LayerMissing, + Reason::AiTrackUnlabelled, + Reason::PlayerChainBroken, + Reason::PresentMismatch, + Reason::PresentOutputIrreproducible, + Reason::ReviewSubjectMismatch, + Reason::ReviewRejected, + Reason::ReviewPathNotExact, + Reason::ReviewReconstructionMismatch, + Reason::LabelAssetMismatch, + Reason::LabelNotShown, + ]; + + /// The conformance code. + pub fn code(self) -> &'static str { + match self { + Reason::RecordMalformed => "record_malformed", + Reason::BadKey => "bad_key", + Reason::BadSignature => "bad_signature", + Reason::ClaimConflict => "claim_conflict", + Reason::ClassMismatch => "class_mismatch", + Reason::SourceMismatch => "source_mismatch", + Reason::InputMismatch => "input_mismatch", + Reason::ClassUnderstated => "class_understated", + Reason::EvidenceInvalid => "evidence_invalid", + Reason::InputSamplesMismatch => "input_samples_mismatch", + Reason::AssistiveRecomputeMismatch => "assistive_recompute_mismatch", + Reason::SamplesMismatch => "samples_mismatch", + Reason::AssistiveNotProved => "assistive_not_proved", + Reason::ClaimUnknown => "claim_unknown", + Reason::MarkingMismatch => "marking_mismatch", + Reason::DetectionMismatch => "detection_mismatch", + Reason::WatermarkAbsent => "watermark_absent", + Reason::LayersDisagree => "layers_disagree", + Reason::LayerMissing => "layer_missing", + Reason::AiTrackUnlabelled => "ai_track_unlabelled", + Reason::PlayerChainBroken => "player_chain_broken", + Reason::PresentMismatch => "present_mismatch", + Reason::PresentOutputIrreproducible => "present_output_irreproducible", + Reason::ReviewSubjectMismatch => "review_subject_mismatch", + Reason::ReviewRejected => "review_rejected", + Reason::ReviewPathNotExact => "review_path_not_exact", + Reason::ReviewReconstructionMismatch => "review_reconstruction_mismatch", + Reason::LabelAssetMismatch => "label_asset_mismatch", + Reason::LabelNotShown => "label_not_shown", + Reason::Room(r) => r.code(), + } + } +} + +impl std::fmt::Display for Reason { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.code()) + } +} + +impl std::error::Error for Reason {} + +// --------------------------------------------------------------------------------------- +// §3 Classes and steps +// --------------------------------------------------------------------------------------- + +/// The four classes, in the lattice's order (§3.1). +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Class { + Captured, + Reconstructed, + Assistive, + Synthesized, +} + +impl Class { + pub const ALL: [Class; 4] = [Class::Captured, Class::Reconstructed, Class::Assistive, Class::Synthesized]; + + pub fn name(self) -> &'static str { + match self { + Class::Captured => "captured", + Class::Reconstructed => "reconstructed", + Class::Assistive => "assistive", + Class::Synthesized => "synthesized", + } + } + + pub fn from_name(s: &str) -> Option { + Class::ALL.into_iter().find(|c| c.name() == s) + } + + /// The first byte of a watermark's payload (§7.2). + pub fn code(self) -> u8 { + self as u8 + } +} + +/// What the claim's signer did to make its subject (§3.2). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Step { + Capture, + Convert, + Assist, + Generate, +} + +impl Step { + pub const ALL: [Step; 4] = [Step::Capture, Step::Convert, Step::Assist, Step::Generate]; + + pub fn name(self) -> &'static str { + match self { + Step::Capture => "capture", + Step::Convert => "convert", + Step::Assist => "assist", + Step::Generate => "generate", + } + } + + pub fn from_name(s: &str) -> Option { + Step::ALL.into_iter().find(|c| c.name() == s) + } + + /// The class a step gives its output on its own (§3.2). + pub fn class(self) -> Class { + match self { + Step::Capture => Class::Captured, + Step::Convert => Class::Reconstructed, + Step::Assist => Class::Assistive, + Step::Generate => Class::Synthesized, + } + } +} + +/// The digital source types a claim may state (§5): those a capture states, then the rest. +pub const CAPTURE_SOURCES: [&str; 4] = ["digitalCapture", "computationalCapture", "screenCapture", "digitalCreation"]; +pub const SOURCES: [&str; 11] = [ + "digitalCapture", + "computationalCapture", + "screenCapture", + "digitalCreation", + "algorithmicallyEnhanced", + "trainedAlgorithmicMedia", + "compositeWithTrainedAlgorithmicMedia", + "algorithmicMedia", + "composite", + "compositeCapture", + "compositeSynthetic", +]; +/// The oversight levels of C2PA 2.4 §18.28.4. +pub const OVERSIGHT: [&str; 3] = ["fully_autonomous", "prompt_guided", "human_validated"]; +pub const MEDIA: [&str; 4] = ["audio", "video", "image", "text"]; + +// --------------------------------------------------------------------------------------- +// §2 Records: encodings, signature, identity +// --------------------------------------------------------------------------------------- + +fn sha256(parts: &[&[u8]]) -> [u8; 32] { + let mut h = Sha256::new(); + for p in parts { + h.update(p); + } + h.finalize().into() +} + +pub fn hex(b: &[u8]) -> String { + b.iter().map(|x| format!("{x:02x}")).collect() +} + +fn unhex(s: &str) -> Option> { + if !s.len().is_multiple_of(2) || !s.bytes().all(|c| c.is_ascii_digit() || (b'a'..=b'f').contains(&c)) { + return None; + } + (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).ok()).collect() +} + +fn hexn(v: &Value) -> Option<[u8; N]> { + unhex(v.as_str()?)?.try_into().ok() +} + +fn int(v: &Value) -> Option { + v.as_u64().filter(|&n| n <= MAX_INTEGER) +} + +fn term(v: &Value) -> Option { + let s = v.as_str()?; + term_ok(s).ok()?; + Some(s.to_string()) +} + +fn one_of(v: &Value, set: &[&str]) -> Option { + let s = v.as_str()?; + set.contains(&s).then(|| s.to_string()) +} + +/// A value the encodings admit (§2): strings, integers, `null`, arrays and objects of them. +fn subset_ok(v: &Value) -> bool { + match v { + Value::Null | Value::String(_) => true, + Value::Number(_) => int(v).is_some(), + Value::Bool(_) => false, + Value::Array(a) => a.iter().all(subset_ok), + Value::Object(o) => o.values().all(subset_ok), + } +} + +/// An object with exactly these members, or `None`. +fn exact<'a>(v: &'a Value, members: &[&str]) -> Option<&'a Map> { + let o = v.as_object()?; + (o.len() == members.len() && members.iter().all(|m| o.contains_key(*m))).then_some(o) +} + +fn nullable(v: &Value, f: impl Fn(&Value) -> Option) -> Option> { + if v.is_null() { Some(None) } else { f(v).map(Some) } +} + +/// The bytes a record is signed over (§2): the domain, then JCS of the record without `sig`. +pub fn signing_bytes(record: &Value) -> Vec { + let mut r = record.clone(); + if let Some(o) = r.as_object_mut() { + o.remove("sig"); + } + let mut m = SIGN_DOMAIN.to_vec(); + m.extend_from_slice(jcs(&r).expect("a record in the subset").as_bytes()); + m +} + +/// A record's link hash (§2): BLAKE3 over the domain and JCS of the whole record. +pub fn link_hash(record: &Value) -> [u8; 32] { + let mut h = blake3::Hasher::new(); + h.update(LINK_DOMAIN); + h.update(jcs(record).expect("a record in the subset").as_bytes()); + *h.finalize().as_bytes() +} + +/// Sign a record body: adds `signer_pubkey` and `sig` (§2). +pub fn sign(mut body: Value, key: &SigningKey) -> Value { + let o = body.as_object_mut().expect("a record is an object"); + o.insert("signer_pubkey".into(), json!(hex(&key.verifying_key().to_bytes()))); + o.remove("sig"); + let sig = key.sign(&signing_bytes(&body)); + body.as_object_mut().expect("an object").insert("sig".into(), json!(hex(&sig.to_bytes()))); + body +} + +/// Read one record's text (§2): strict JSON, an object of `kind`'s exact members, then the +/// signature. Returns the value, its signer and its link hash. +fn read_record(text: &str, kind: &str, members: &[&str]) -> Result<(Value, [u8; 32], [u8; 32]), Reason> { + let v = crate::claim_text::read(text).map_err(|_| Reason::RecordMalformed)?; + let o = exact(&v, members).ok_or(Reason::RecordMalformed)?; + if o.get("kind").and_then(Value::as_str) != Some(kind) || !subset_ok(&v) { + return Err(Reason::RecordMalformed); + } + let signer: [u8; 32] = hexn(&o["signer_pubkey"]).ok_or(Reason::RecordMalformed)?; + if hexn::<64>(&o["sig"]).is_none() { + return Err(Reason::RecordMalformed); + } + if let Some(at) = o.get("issued_at") { + if !at.as_str().is_some_and(rfc3339) { + return Err(Reason::RecordMalformed); + } + } + let link = link_hash(&v); + Ok((v, signer, link)) +} + +fn check_signature(v: &Value, signer: &[u8; 32]) -> Result<(), Reason> { + let sig: [u8; 64] = hexn(&v["sig"]).ok_or(Reason::RecordMalformed)?; + crate::sig::verify_strict(signer, &signing_bytes(v), &sig).map_err(|e| match e { + crate::sig::SigError::BadKey => Reason::BadKey, + crate::sig::SigError::BadSignature => Reason::BadSignature, + }) +} + +// --------------------------------------------------------------------------------------- +// §3–§4 The origin claim +// --------------------------------------------------------------------------------------- + +/// What a claim is about (§3.1). +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Subject { + /// An object, by its content hash (jwp-receipts §2.1). + Object([u8; 32]), + /// A group, by its group receipt's link hash (jwp-receipts §3 step 3). + Group([u8; 32]), + /// A track from one group on, by the track's digest (live-rooms §6.2). + Track { track: [u8; 32], from_group: u64 }, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Model { + pub digest: [u8; 32], + pub trained: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Input { + pub content_hash: [u8; 32], + pub origin: Option<[u8; 32]>, + pub class: Class, + pub source: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Evidence { + pub replicate: bool, + pub derivation: String, + pub params: Value, + pub width: u64, + pub height: u64, + pub input_samples: [u8; 32], + pub output_samples: [u8; 32], +} + +/// An origin claim, read (§3.1). +#[derive(Clone, Debug)] +pub struct Origin { + pub value: Value, + pub link: [u8; 32], + pub signer: [u8; 32], + pub subject: Subject, + pub media: String, + pub class: Class, + pub step: Step, + pub operation: String, + pub record: Option<[u8; 32]>, + pub source: String, + pub models: Vec, + pub oversight: Option, + pub inputs: Vec, + pub evidence: Option, +} + +pub const ORIGIN_MEMBERS: [&str; 15] = [ + "kind", "subject", "media", "class", "step", "operation", "record", "source", "models", "oversight", "inputs", "evidence", "issued_at", + "signer_pubkey", "sig", +]; + +fn read_subject(v: &Value) -> Option { + match v.get("type")?.as_str()? { + "object" => Some(Subject::Object(hexn(&exact(v, &["type", "content_hash"])?["content_hash"])?)), + "group" => Some(Subject::Group(hexn(&exact(v, &["type", "receipt"])?["receipt"])?)), + "track" => { + let o = exact(v, &["type", "track", "from_group"])?; + Some(Subject::Track { track: hexn(&o["track"])?, from_group: int(&o["from_group"])? }) + } + _ => None, + } +} + +fn read_evidence(v: &Value) -> Option { + let o = exact(v, &["tier", "derivation", "params", "width", "height", "input_samples", "output_samples"])?; + let replicate = match o["tier"].as_str()? { + "replicate" => true, + "attest" => false, + _ => return None, + }; + let (width, height) = (int(&o["width"])?, int(&o["height"])?); + if width == 0 || height == 0 || !o["params"].is_object() { + return None; + } + Some(Evidence { + replicate, + derivation: term(&o["derivation"])?, + params: o["params"].clone(), + width, + height, + input_samples: hexn(&o["input_samples"])?, + output_samples: hexn(&o["output_samples"])?, + }) +} + +/// §3.1's reading: every member of its type and range, and §3.2's constraints of the step. +fn read_origin_fields(v: &Value, link: [u8; 32], signer: [u8; 32]) -> Option { + let o = v.as_object()?; + let models: Vec = o["models"] + .as_array()? + .iter() + .map(|m| { + let m = exact(m, &["digest", "type"])?; + let trained = match m["type"].as_str()? { + "trained" => true, + "algorithmic" => false, + _ => return None, + }; + Some(Model { digest: hexn(&m["digest"])?, trained }) + }) + .collect::>()?; + let inputs: Vec = o["inputs"] + .as_array()? + .iter() + .map(|i| { + let i = exact(i, &["content_hash", "origin", "class", "source"])?; + Some(Input { + content_hash: hexn(&i["content_hash"])?, + origin: nullable(&i["origin"], hexn)?, + class: Class::from_name(i["class"].as_str()?)?, + source: one_of(&i["source"], &SOURCES)?, + }) + }) + .collect::>()?; + let c = Origin { + value: v.clone(), + link, + signer, + subject: read_subject(&o["subject"])?, + media: one_of(&o["media"], &MEDIA)?, + class: Class::from_name(o["class"].as_str()?)?, + step: Step::from_name(o["step"].as_str()?)?, + operation: term(&o["operation"])?, + record: nullable(&o["record"], hexn)?, + source: one_of(&o["source"], &SOURCES)?, + oversight: nullable(&o["oversight"], |x| one_of(x, &OVERSIGHT))?, + evidence: nullable(&o["evidence"], read_evidence)?, + models, + inputs, + }; + let distinct_models = c.models.iter().map(|m| m.digest).collect::>().len() == c.models.len(); + let distinct_inputs = c.inputs.iter().map(|i| i.content_hash).collect::>().len() == c.inputs.len(); + if c.models.len() > MAX_LIST || c.inputs.len() > MAX_LIST || !distinct_models || !distinct_inputs { + return None; + } + let (n, m) = (c.inputs.len(), c.models.len()); + let ok = match c.step { + Step::Capture => n == 0 && m == 0 && c.oversight.is_none() && c.evidence.is_none() && CAPTURE_SOURCES.contains(&c.source.as_str()), + Step::Convert => n >= 1 && m == 0 && c.oversight.is_none() && c.evidence.is_none(), + Step::Assist => n == 1 && c.oversight.is_none() && c.evidence.is_some(), + Step::Generate => m >= 1 && c.oversight.is_some() && c.evidence.is_none(), + }; + ok.then_some(c) +} + +/// Read an origin claim's text (§3.1, §2). +pub fn read_origin(text: &str) -> Result { + let (v, signer, link) = read_record(text, "wai-origin", &ORIGIN_MEMBERS)?; + let c = read_origin_fields(&v, link, signer).ok_or(Reason::RecordMalformed)?; + check_signature(&v, &signer)?; + Ok(c) +} + +impl Origin { + /// §3.2: the greater of the step's class and its inputs' classes. + pub fn composed_class(&self) -> Class { + self.inputs.iter().map(|i| i.class).fold(self.step.class(), Ord::max) + } + + /// §5: the source a generation gives its output. + pub fn generated_source(&self) -> &'static str { + if !self.models.iter().any(|m| m.trained) { + "algorithmicMedia" + } else if self.oversight.as_deref() == Some("human_validated") || self.inputs.iter().any(|i| i.class != Class::Synthesized) { + "compositeWithTrainedAlgorithmicMedia" + } else { + "trainedAlgorithmicMedia" + } + } + + /// §5: the digital source type of the subject, a function of the claim. + pub fn computed_source(&self) -> String { + match self.step { + Step::Capture => self.source.clone(), + Step::Convert if self.inputs.len() == 1 => self.inputs[0].source.clone(), + Step::Convert => { + if self.inputs.iter().any(|i| i.class == Class::Synthesized) { + "compositeSynthetic".into() + } else if self.inputs.iter().all(|i| matches!(i.source.as_str(), "digitalCapture" | "computationalCapture")) { + "compositeCapture".into() + } else { + "composite".into() + } + } + Step::Assist if self.inputs[0].class == Class::Synthesized => self.inputs[0].source.clone(), + Step::Assist => "algorithmicallyEnhanced".into(), + Step::Generate => self.generated_source().into(), + } + } + + /// §6: the C2PA statement, `{"actions": [...], "assertions": [...]}`, as a JSON value. + pub fn c2pa_statement(&self) -> Value { + let dst = |code: &str| format!("{IPTC}{code}"); + let actions = match (self.inputs.len(), self.step) { + (1, Step::Convert) => json!([{"action": "c2pa.opened"}, {"action": "c2pa.converted"}]), + (1, Step::Assist) => json!([{"action": "c2pa.opened"}, {"action": "c2pa.edited", "digitalSourceType": dst("algorithmicallyEnhanced")}]), + (1, Step::Generate) => json!([{"action": "c2pa.opened"}, {"action": "c2pa.edited", "digitalSourceType": dst(self.generated_source())}]), + _ => json!([{"action": "c2pa.created", "digitalSourceType": dst(&self.source)}]), + }; + let assertions: Vec = self + .models + .iter() + .filter(|m| m.trained) + .enumerate() + .map(|(i, m)| { + let label = if i == 0 { "c2pa.ai-disclosure".to_string() } else { format!("c2pa.ai-disclosure__{i}") }; + let mut data = json!({"modelType": "c2pa.types.model", "modelIdentifier": format!("{MODEL_ID}{}", hex(&m.digest))}); + if self.step == Step::Generate { + data["contentProfile"] = json!({"humanOversightLevel": self.oversight.clone()}); + } + json!({"label": label, "data": data}) + }) + .collect(); + json!({"actions": actions, "assertions": assertions}) + } + + /// §6: the statement written as its JCS text. + pub fn export(&self) -> String { + jcs(&self.c2pa_statement()).expect("a statement in the subset") + } + + /// §7.2: the payload a watermark carrying this claim holds: the class's code, then the + /// first seven bytes of the claim's link hash. + pub fn mark_payload(&self) -> [u8; 8] { + let mut p = [0u8; 8]; + p[0] = self.class.code(); + p[1..].copy_from_slice(&self.link[..7]); + p + } +} + +// --------------------------------------------------------------------------------------- +// §4 The pinned integer derivations +// --------------------------------------------------------------------------------------- + +/// `wai-synthesis:upscale2` (§4.1): a `w` × `h` RGB8 buffer to `2w` × `2h`, each new sample +/// the rounded mean of its neighbours, the last column and row repeated. +pub fn upscale2(input: &[u8], w: usize, h: usize) -> Vec { + let at = |x: usize, y: usize, c: usize| input[(y * w + x) * 3 + c] as u32; + let ow = 2 * w; + let mut out = vec![0u8; 4 * w * h * 3]; + for y in 0..h { + for x in 0..w { + let (x1, y1) = ((x + 1).min(w - 1), (y + 1).min(h - 1)); + for c in 0..3 { + let (a, b, cc, d) = (at(x, y, c), at(x1, y, c), at(x, y1, c), at(x1, y1, c)); + out[((2 * y) * ow + 2 * x) * 3 + c] = a as u8; + out[((2 * y) * ow + 2 * x + 1) * 3 + c] = ((a + b + 1) >> 1) as u8; + out[((2 * y + 1) * ow + 2 * x) * 3 + c] = ((a + cc + 1) >> 1) as u8; + out[((2 * y + 1) * ow + 2 * x + 1) * 3 + c] = ((a + b + cc + d + 2) >> 2) as u8; + } + } + } + out +} + +/// `wai-synthesis:denoise3` (§4.2): each sample the 3 × 3 binomial mean of its +/// neighbourhood, weights 1 2 1 / 2 4 2 / 1 2 1, edges clamped, rounded by adding 8 before +/// the shift by 4. +pub fn denoise3(input: &[u8], w: usize, h: usize) -> Vec { + const K: [[u32; 3]; 3] = [[1, 2, 1], [2, 4, 2], [1, 2, 1]]; + let mut out = vec![0u8; w * h * 3]; + for y in 0..h { + for x in 0..w { + for c in 0..3 { + let mut s = 8u32; + for (j, row) in K.iter().enumerate() { + for (i, k) in row.iter().enumerate() { + let xx = (x + i).saturating_sub(1).min(w - 1); + let yy = (y + j).saturating_sub(1).min(h - 1); + s += k * input[(yy * w + xx) * 3 + c] as u32; + } + } + out[(y * w + x) * 3 + c] = (s >> 4) as u8; + } + } + } + out +} + +/// §4.3: whether replicate evidence names a pinned derivation with its parameters and a +/// size within [`MAX_SAMPLES`]. +fn replicate_evidence_ok(e: &Evidence) -> bool { + let factor = match e.derivation.as_str() { + UPSCALE2 => 4, + DENOISE3 => 1, + _ => return false, + }; + let empty = e.params.as_object().is_some_and(Map::is_empty); + let size = e.width.checked_mul(e.height).and_then(|n| n.checked_mul(3 * factor)); + empty && size.is_some_and(|n| n <= MAX_SAMPLES) +} + +/// Run a pinned derivation over a buffer the evidence sizes. +pub fn derive(derivation: &str, input: &[u8], w: usize, h: usize) -> Option> { + match derivation { + UPSCALE2 => Some(upscale2(input, w, h)), + DENOISE3 => Some(denoise3(input, w, h)), + _ => None, + } +} + +// --------------------------------------------------------------------------------------- +// §7 Marking and detection +// --------------------------------------------------------------------------------------- + +pub const MARKING_MEMBERS: [&str; 9] = ["kind", "subject", "origin", "scheme", "scheme_digest", "payload", "issued_at", "signer_pubkey", "sig"]; +pub const DETECTION_MEMBERS: [&str; 8] = ["kind", "subject", "scheme", "scheme_digest", "payload", "issued_at", "signer_pubkey", "sig"]; +/// The scheme the conformance corpus names (§7.4): a slot, with no algorithm. +pub const CONFORMANCE_SCHEME: &str = "wai-mark:conformance"; + +#[derive(Clone, Debug)] +pub struct Marking { + pub link: [u8; 32], + pub subject: [u8; 32], + pub origin: [u8; 32], + pub scheme: String, + pub scheme_digest: [u8; 32], + pub payload: [u8; 8], +} + +#[derive(Clone, Debug)] +pub struct Detection { + pub link: [u8; 32], + pub subject: [u8; 32], + pub scheme: String, + pub scheme_digest: [u8; 32], + pub payload: Option<[u8; 8]>, +} + +pub fn read_marking(text: &str) -> Result { + let (v, signer, link) = read_record(text, "wai-marking", &MARKING_MEMBERS)?; + let f = || { + Some(Marking { + link, + subject: hexn(&v["subject"])?, + origin: hexn(&v["origin"])?, + scheme: term(&v["scheme"])?, + scheme_digest: hexn(&v["scheme_digest"])?, + payload: hexn(&v["payload"])?, + }) + }; + let m = f().ok_or(Reason::RecordMalformed)?; + check_signature(&v, &signer)?; + Ok(m) +} + +pub fn read_detection(text: &str) -> Result { + let (v, signer, link) = read_record(text, "wai-detection", &DETECTION_MEMBERS)?; + let f = || { + Some(Detection { + link, + subject: hexn(&v["subject"])?, + scheme: term(&v["scheme"])?, + scheme_digest: hexn(&v["scheme_digest"])?, + payload: nullable(&v["payload"], hexn)?, + }) + }; + let d = f().ok_or(Reason::RecordMalformed)?; + check_signature(&v, &signer)?; + Ok(d) +} + +// --------------------------------------------------------------------------------------- +// §9 Reviews +// --------------------------------------------------------------------------------------- + +pub const REVIEW_MEMBERS: [&str; 14] = + ["kind", "subject", "origin", "tier", "reconstruction", "decoder", "variant", "scope", "verdict", "notes", "role", "issued_at", "signer_pubkey", "sig"]; +pub const REVIEW_TIERS: [&str; 2] = ["decode-equivalence", "generation-equivalence"]; +pub const PATH_TIERS: [&str; 4] = ["decode-equivalence", "generation-equivalence", "entropy-consistency", "none"]; + +#[derive(Clone, Debug)] +pub struct Review { + pub link: [u8; 32], + pub signer: [u8; 32], + pub subject: [u8; 32], + pub origin: [u8; 32], + pub tier: String, + pub reconstruction: [u8; 32], + pub scope: String, + pub verdict: String, +} + +pub fn read_review(text: &str) -> Result { + let (v, signer, link) = read_record(text, "wai-review", &REVIEW_MEMBERS)?; + let f = || { + let verdict = one_of(&v["verdict"], &["approved", "approved-with-notes", "rejected"])?; + let notes: Option<[u8; 32]> = nullable(&v["notes"], hexn)?; + if notes.is_none() != (verdict == "approved") { + return None; + } + nullable(&v["variant"], term)?; + nullable(&v["role"], term)?; + term(&v["decoder"])?; + Some(Review { + link, + signer, + subject: hexn(&v["subject"])?, + origin: hexn(&v["origin"])?, + tier: one_of(&v["tier"], &REVIEW_TIERS)?, + reconstruction: hexn(&v["reconstruction"])?, + scope: one_of(&v["scope"], &["accuracy", "rendering", "translation"])?, + verdict, + }) + }; + let r = f().ok_or(Reason::RecordMalformed)?; + check_signature(&v, &signer)?; + Ok(r) +} + +// --------------------------------------------------------------------------------------- +// §10 Label display +// --------------------------------------------------------------------------------------- + +pub const POLICY_MEMBERS: [&str; 9] = ["kind", "classes", "visual", "audio", "interval_us", "grace_us", "issued_at", "signer_pubkey", "sig"]; +pub const PLAYER_MEMBERS: [&str; 8] = ["kind", "session", "seq", "prev", "at", "entry", "signer_pubkey", "sig"]; + +/// The EU icons of the Code of Practice's Annex 1, pinned by SHA-256 as published (§10.2). +pub const EU_ICONS: [(&str, &str); 24] = [ + ("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"), +]; + +/// The pinned icon a digest is, if it is one (§10.2). +pub fn eu_icon(digest: &[u8; 32]) -> Option<&'static str> { + let h = hex(digest); + EU_ICONS.iter().find(|(_, d)| *d == h).map(|(n, _)| *n) +} + +#[derive(Clone, Debug)] +pub struct LabelPolicy { + pub classes: Vec, + pub visual: [u8; 32], + pub audio: Option<[u8; 32]>, + pub interval_us: Option, + pub grace_us: u64, +} + +pub fn read_label_policy(text: &str) -> Result { + let (v, signer, _) = read_record(text, "wai-label-policy", &POLICY_MEMBERS)?; + let f = || { + let classes: Vec = v["classes"].as_array()?.iter().map(|c| Class::from_name(c.as_str()?)).collect::>()?; + if classes.is_empty() || !classes.windows(2).all(|w| w[0] < w[1]) { + return None; + } + let interval_us = nullable(&v["interval_us"], int)?; + if interval_us == Some(0) { + return None; + } + Some(LabelPolicy { classes, visual: hexn(&v["visual"])?, audio: nullable(&v["audio"], hexn)?, interval_us, grace_us: int(&v["grace_us"])? }) + }; + let p = f().ok_or(Reason::RecordMalformed)?; + check_signature(&v, &signer)?; + Ok(p) +} + +/// One entry of a player's log (§10.3). +#[derive(Clone, Debug)] +pub enum Entry { + Present { subject: [u8; 32], origin: Option<[u8; 32]>, class: Option, media: String, tier: String, output: Option<[u8; 32]> }, + Shown { audio: bool, asset: [u8; 32], trigger: String }, + Resume, + Review { subject: [u8; 32], review: [u8; 32] }, +} + +#[derive(Clone, Debug)] +pub struct PlayerEntry { + pub link: [u8; 32], + pub signer: [u8; 32], + pub session: [u8; 32], + pub seq: u64, + pub prev: Option<[u8; 32]>, + pub at: u64, + pub entry: Entry, +} + +fn read_entry(v: &Value) -> Option { + match v.get("type")?.as_str()? { + "present" => { + let o = exact(v, &["type", "subject", "origin", "class", "media", "tier", "decoder", "variant", "output"])?; + term(&o["decoder"])?; + nullable(&o["variant"], term)?; + Some(Entry::Present { + subject: hexn(&o["subject"])?, + origin: nullable(&o["origin"], hexn)?, + class: nullable(&o["class"], |c| Class::from_name(c.as_str()?))?, + media: one_of(&o["media"], &MEDIA)?, + tier: one_of(&o["tier"], &PATH_TIERS)?, + output: nullable(&o["output"], hexn)?, + }) + } + "shown" => { + let o = exact(v, &["type", "label", "asset", "trigger"])?; + let audio = match o["label"].as_str()? { + "visual" => false, + "audio" => true, + _ => return None, + }; + Some(Entry::Shown { audio, asset: hexn(&o["asset"])?, trigger: one_of(&o["trigger"], &["first-exposure", "resume", "interval"])? }) + } + "resume" => { + let o = exact(v, &["type", "after"])?; + one_of(&o["after"], &["ad-break", "other"])?; + Some(Entry::Resume) + } + "review" => { + let o = exact(v, &["type", "subject", "review"])?; + Some(Entry::Review { subject: hexn(&o["subject"])?, review: hexn(&o["review"])? }) + } + _ => None, + } +} + +pub fn read_player_entry(text: &str) -> Result { + let (v, signer, link) = read_record(text, "wai-player", &PLAYER_MEMBERS)?; + let f = || { + Some(PlayerEntry { + link, + signer, + session: hexn(&v["session"])?, + seq: int(&v["seq"])?, + prev: nullable(&v["prev"], hexn)?, + at: int(&v["at"])?, + entry: read_entry(&v["entry"])?, + }) + }; + let e = f().ok_or(Reason::RecordMalformed)?; + check_signature(&v, &signer)?; + Ok(e) +} + +// --------------------------------------------------------------------------------------- +// §11 Verification +// --------------------------------------------------------------------------------------- + +/// A room a case holds (§11.1): live-rooms §9.1's inputs. +#[derive(Clone, Debug, Default)] +pub struct RoomInput { + pub descriptor: Vec, + pub grants: Vec>, + pub head: Option, + pub chains: Vec>>, +} + +/// A verifier's policy (§11.1). +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct Policy { + /// `dual_layer` `required`: every synthesized object's claim has a marking. + pub dual_layer_required: bool, + /// `assistive` `require-proof`: every assistive claim's evidence is recomputed. + pub require_proof: bool, +} + +/// What a verifier is given (§11.1). +#[derive(Clone, Debug, Default)] +pub struct Case { + pub claims: Vec, + pub markings: Vec, + pub detections: Vec, + pub reviews: Vec, + pub label_policy: Option, + pub player: Vec, + /// Envelopes the verifier holds; each is named by its content hash. + pub objects: Vec>, + /// Canonical buffers a sink decoded, by the content hash of the object decoded. + pub decoded: Vec<([u8; 32], Vec)>, + pub room: Option, + pub policy: Policy, +} + +/// Where a case is refused (§11.2). +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum At { + Claim(usize), + Marking(usize), + Detection(usize), + Review(usize), + LabelPolicy, + Player(usize), + Room(room::At), +} + +impl At { + pub fn to_json(&self) -> Value { + match self { + At::Claim(i) => json!(["claim", i]), + At::Marking(i) => json!(["marking", i]), + At::Detection(i) => json!(["detection", i]), + At::Review(i) => json!(["review", i]), + At::LabelPolicy => json!(["label_policy"]), + At::Player(i) => json!(["player", i]), + At::Room(a) => match a { + room::At::Descriptor => json!(["room", "descriptor"]), + room::At::KeyLog => json!(["room", "key_log"]), + room::At::Grant(i) => json!(["room", "grant", i]), + room::At::Record { chain, record } => json!(["room", "record", chain, record]), + }, + } + } +} + +/// What an accepted case states (§11.3). +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Report { + pub claims: Vec, + pub detections: Vec, + pub ai_tracks: Vec, + pub reviews_presented: Vec, + pub labels: Vec, + pub visual_icon: Option, + pub room: Option<&'static str>, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Outcome { + Accept(Report), + Refuse { at: At, reason: Reason }, +} + +type Step6 = Result; + +/// The manifest of a `WAI1` envelope (SPEC §2), or `None` for bytes that are not one. +fn envelope_manifest(b: &[u8]) -> Option { + if b.len() < 8 || &b[..4] != b"WAI1" { + return None; + } + let n = u32::from_le_bytes(b[4..8].try_into().ok()?) as usize; + serde_json::from_slice(b.get(8..8 + n)?).ok() +} + +/// §3.3: whether an envelope's capability generates: its registry class is +/// `GenerativeDeterministic` or `Behavioral`, or its `intent` is `create`. +pub fn generates(envelope: &[u8]) -> bool { + use crate::codecs::ReplicateClass as R; + let Some(m) = envelope_manifest(envelope) else { return false }; + let cap = m["model_requirement"]["capability"].as_str().unwrap_or(""); + matches!(crate::codecs::capability_replicate_class(cap), Some(R::GenerativeDeterministic | R::Behavioral)) || m["intent"] == "create" +} + +/// The digest of a room track (live-rooms §6.2): SHA-256 over the rendering of its +/// namespace, one zero byte, and its name. +pub fn track_digest(room_id: &[u8; 32], owner: &str, media: &str, layer: u8) -> [u8; 32] { + let ns = vec![hex(room_id), owner.to_string(), media.to_string()]; + sha256(&[room::render_namespace(&ns).as_bytes(), &[0], layer.to_string().as_bytes()]) +} + +/// Verify a case (§11.2). +pub fn verify(case: &Case) -> Outcome { + match run(case) { + Ok(r) => Outcome::Accept(r), + Err((at, reason)) => Outcome::Refuse { at, reason }, + } +} + +fn run(case: &Case) -> Step6 { + let mut report = Report::default(); + // Step 1: every record reads and its signature verifies. + let claims: Vec = case.claims.iter().enumerate().map(|(i, t)| read_origin(t).map_err(|r| (At::Claim(i), r))).collect::>()?; + let markings: Vec = case.markings.iter().enumerate().map(|(i, t)| read_marking(t).map_err(|r| (At::Marking(i), r))).collect::>()?; + let detections: Vec = + case.detections.iter().enumerate().map(|(i, t)| read_detection(t).map_err(|r| (At::Detection(i), r))).collect::>()?; + let reviews: Vec = case.reviews.iter().enumerate().map(|(i, t)| read_review(t).map_err(|r| (At::Review(i), r))).collect::>()?; + let label_policy = match &case.label_policy { + Some(t) => Some(read_label_policy(t).map_err(|r| (At::LabelPolicy, r))?), + None => None, + }; + let player: Vec = case.player.iter().enumerate().map(|(i, t)| read_player_entry(t).map_err(|r| (At::Player(i), r))).collect::>()?; + + let by_link: BTreeMap<[u8; 32], &Origin> = claims.iter().map(|c| (c.link, c)).collect(); + let objects: BTreeMap<[u8; 32], &Vec> = case.objects.iter().map(|o| (crate::jwp_hash::content_hash(o), o)).collect(); + let decoded: BTreeMap<[u8; 32], &Vec> = case.decoded.iter().map(|(h, b)| (*h, b)).collect(); + + // Step 2: each origin claim. + let mut subjects = BTreeSet::new(); + for (i, c) in claims.iter().enumerate() { + let at = At::Claim(i); + let e = |r| (at.clone(), r); + if !subjects.insert(jcs(&c.value["subject"]).expect("a subject in the subset")) { + return Err(e(Reason::ClaimConflict)); + } + if c.class != c.composed_class() { + return Err(e(Reason::ClassMismatch)); + } + if c.source != c.computed_source() { + return Err(e(Reason::SourceMismatch)); + } + for input in &c.inputs { + if let Some(held) = input.origin.and_then(|h| by_link.get(&h)) { + if held.subject != Subject::Object(input.content_hash) || held.class != input.class || held.source != input.source { + return Err(e(Reason::InputMismatch)); + } + } + } + if let Subject::Object(h) = c.subject { + if objects.get(&h).is_some_and(|env| generates(env)) && c.step != Step::Generate { + return Err(e(Reason::ClassUnderstated)); + } + } + let mut status = Value::Null; + if let Some(ev) = &c.evidence { + if ev.replicate && !replicate_evidence_ok(ev) { + return Err(e(Reason::EvidenceInvalid)); + } + status = json!(if ev.replicate { "not-recomputed" } else { "asserted" }); + if ev.replicate { + if let Some(input) = decoded.get(&c.inputs[0].content_hash) { + let (w, h) = (ev.width as usize, ev.height as usize); + if input.len() != w * h * 3 || sha256(&[input]) != ev.input_samples { + return Err(e(Reason::InputSamplesMismatch)); + } + let out = derive(&ev.derivation, input, w, h).expect("a pinned derivation"); + if sha256(&[&out]) != ev.output_samples { + return Err(e(Reason::AssistiveRecomputeMismatch)); + } + status = json!("proved"); + } + } + if let Subject::Object(h) = c.subject { + if decoded.get(&h).is_some_and(|b| sha256(&[b]) != ev.output_samples) { + return Err(e(Reason::SamplesMismatch)); + } + } + if case.policy.require_proof && status != "proved" { + return Err(e(Reason::AssistiveNotProved)); + } + } + report.claims.push(json!({ + "link": hex(&c.link), + "class": c.class.name(), + "source": c.source, + "c2pa": c.export(), + "assistive": status, + })); + } + + // Step 3: markings, detections, and the two layers. + for (i, m) in markings.iter().enumerate() { + let c = by_link.get(&m.origin).ok_or((At::Marking(i), Reason::ClaimUnknown))?; + if c.subject != Subject::Object(m.subject) || c.mark_payload() != m.payload { + return Err((At::Marking(i), Reason::MarkingMismatch)); + } + } + for (i, d) in detections.iter().enumerate() { + let e = |r| (At::Detection(i), r); + let claim = claims.iter().find(|c| c.subject == Subject::Object(d.subject)); + let marking = markings.iter().find(|m| m.subject == d.subject); + if let Some(m) = marking { + if m.scheme != d.scheme || m.scheme_digest != d.scheme_digest { + return Err(e(Reason::DetectionMismatch)); + } + } + let result = match (d.payload, claim) { + (None, _) if marking.is_some() => return Err(e(Reason::WatermarkAbsent)), + (None, _) => "unmarked", + (Some(_), None) => "not-checked", + (Some(p), Some(c)) if p != c.mark_payload() => return Err(e(Reason::LayersDisagree)), + (Some(_), Some(_)) => "agree", + }; + report.detections.push(json!({"subject": hex(&d.subject), "result": result})); + } + if case.policy.dual_layer_required { + for (i, c) in claims.iter().enumerate() { + if c.class == Class::Synthesized && matches!(c.subject, Subject::Object(_)) && !markings.iter().any(|m| m.origin == c.link) { + return Err((At::Claim(i), Reason::LayerMissing)); + } + } + } + + // Step 4: reviews. + for (i, r) in reviews.iter().enumerate() { + let c = by_link.get(&r.origin).ok_or((At::Review(i), Reason::ClaimUnknown))?; + if c.subject != Subject::Object(r.subject) || c.class != Class::Synthesized { + return Err((At::Review(i), Reason::ReviewSubjectMismatch)); + } + } + + // Step 5: the room, and the tracks of its AI participants. + if let Some(ri) = &case.room { + let run = room::verify_room(&ri.descriptor, &ri.grants, ri.head.as_deref(), &ri.chains); + report.room = Some(match &run.outcome { + room::RoomOutcome::Refuse { at, reason } => return Err((At::Room(*at), Reason::Room(*reason))), + room::RoomOutcome::Gap(_) => "gap", + room::RoomOutcome::Accept => "accept", + }); + let room_id = run.room.expect("a room that read"); + let d = run.descriptor.as_ref().expect("a descriptor that read"); + let grants: BTreeMap<[u8; 32], &room::Grant> = run.grants.iter().map(|h| (h.digest, &h.grant)).collect(); + let mut current: BTreeMap<[u8; 32], [u8; 32]> = BTreeMap::new(); + for &(chain, record, under) in &run.order { + if under.is_some() { + continue; + } + let ev = Event::parse(&ri.chains[chain][record]).expect("a record the room accepted"); + let Ok(Payload::Lifecycle { kind, subject, detail }) = crate::live_events::parse_payload(ev.kind, &ev.payload, u16::MAX) else { continue }; + let Ok(detail) = Detail::parse(kind, &detail) else { continue }; + let (slot, m, layers) = match detail { + Detail::Join { grant } => { + current.insert(subject, grant); + continue; + } + Detail::GrantExchange { new, .. } => { + current.insert(subject, new); + continue; + } + Detail::Leave { .. } => { + current.remove(&subject); + continue; + } + Detail::Publish { slot, media, layers, .. } | Detail::Takeover { slot, media, layers, .. } => (slot, media, layers), + _ => continue, + }; + let Some(model) = current.get(&subject).and_then(|g| grants.get(g)).and_then(|g| g.ai_model()) else { continue }; + let owner = if slot.is_empty() { hex(&subject) } else { format!("_{slot}") }; + let media = room::media_name(m).expect("a media kind the room accepted"); + for layer in 0..layers { + let track = track_digest(&room_id, &owner, media, layer); + let found = claims.iter().find(|c| { + matches!(c.subject, Subject::Track { track: t, .. } if t == track) + && c.class == Class::Synthesized + && c.step == Step::Generate + && c.models.iter().any(|x| x.digest == model) + && (c.signer == subject || d.is_room_key(&c.signer)) + }); + match found { + Some(c) => report.ai_tracks.push(json!({"track": hex(&track), "claim": hex(&c.link)})), + None => return Err((At::Room(room::At::Record { chain, record }), Reason::AiTrackUnlabelled)), + } + } + } + } + + // Step 6: the player's log. + for (j, p) in player.iter().enumerate() { + let first = &player[0]; + let ok = p.seq == j as u64 + && p.session == first.session + && p.signer == first.signer + && if j == 0 { p.prev.is_none() } else { p.prev == Some(player[j - 1].link) && p.at >= player[j - 1].at }; + if !ok { + return Err((At::Player(j), Reason::PlayerChainBroken)); + } + } + for (j, p) in player.iter().enumerate() { + let e = |r| (At::Player(j), r); + match &p.entry { + Entry::Present { subject, origin, class, output, .. } => { + if let Some(c) = origin.and_then(|h| by_link.get(&h)) { + if c.subject != Subject::Object(*subject) || *class != Some(c.class) { + return Err(e(Reason::PresentMismatch)); + } + } + if let (Some(o), Some(b)) = (output, decoded.get(subject)) { + if sha256(&[b]) != *o { + return Err(e(Reason::PresentOutputIrreproducible)); + } + } + } + Entry::Review { subject, review } => { + let r = reviews.iter().find(|r| r.link == *review).ok_or(e(Reason::ClaimUnknown))?; + let shown = player[..j].iter().rev().find_map(|q| match &q.entry { + Entry::Present { subject: s, tier, output, .. } if s == subject => Some((tier, output)), + _ => None, + }); + let Some((tier, output)) = shown.filter(|_| r.subject == *subject) else { + return Err(e(Reason::ReviewSubjectMismatch)); + }; + if r.verdict == "rejected" { + return Err(e(Reason::ReviewRejected)); + } + if *tier != r.tier { + return Err(e(Reason::ReviewPathNotExact)); + } + if *output != Some(r.reconstruction) { + return Err(e(Reason::ReviewReconstructionMismatch)); + } + report.reviews_presented.push(json!({"entry": j, "review": hex(review)})); + } + _ => {} + } + } + if let Some(lp) = &label_policy { + report.visual_icon = eu_icon(&lp.visual).map(String::from); + labels(&player, lp, &mut report)?; + } + Ok(report) +} + +/// §10.4: the label obligations of a player's log under a label policy. +fn labels(player: &[PlayerEntry], lp: &LabelPolicy, report: &mut Report) -> Step6<()> { + for (j, p) in player.iter().enumerate() { + if let Entry::Shown { audio, asset, .. } = &p.entry { + let want = if *audio { lp.audio } else { Some(lp.visual) }; + if want != Some(*asset) { + return Err((At::Player(j), Reason::LabelAssetMismatch)); + } + } + } + let shown_at = |audio: bool, lo: u64, hi: u64| { + player.iter().position(|q| matches!(q.entry, Entry::Shown { audio: a, .. } if a == audio) && lo <= q.at && q.at <= hi) + }; + // The index of the last labelled presentation of each kind (visual, audio). + let mut last: [Option; 2] = [None, None]; + for (j, p) in player.iter().enumerate() { + let Entry::Present { class: Some(class), media, .. } = &p.entry else { continue }; + if !lp.classes.contains(class) { + continue; + } + let audio = media == "audio"; + let hi = p.at.saturating_add(lp.grace_us); + let resumed = |from: usize| player[from..j].iter().rposition(|q| matches!(q.entry, Entry::Resume)).map(|k| player[from + k].at); + let (trigger, found) = match last[audio as usize] { + None => ("first-exposure", shown_at(audio, 0, hi)), + Some(prev) => match resumed(prev + 1) { + Some(r) => ("resume", shown_at(audio, r, hi)), + None => match lp.interval_us { + Some(iv) => ("interval", shown_at(audio, p.at.saturating_sub(iv), hi)), + None => { + last[audio as usize] = Some(j); + continue; + } + }, + }, + }; + let Some(k) = found else { return Err((At::Player(j), Reason::LabelNotShown)) }; + report.labels.push(json!({"entry": j, "trigger": trigger, "shown": k})); + last[audio as usize] = Some(j); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn the_derivations_are_the_rules_of_section_4() { + // A 2 × 1 image: one black and one white pixel. + let input = [0, 0, 0, 255, 255, 255]; + let up = upscale2(&input, 2, 1); + assert_eq!(up.len(), 4 * 2 * 3); + // Row 0: a, (a+b+1)>>1, b, b (the last column repeats). + assert_eq!(&up[..12], &[0, 0, 0, 128, 128, 128, 255, 255, 255, 255, 255, 255]); + // Row 1 repeats row 0, the last row being its own neighbour. + assert_eq!(&up[12..], &up[..12]); + let flat = vec![77u8; 5 * 4 * 3]; + assert_eq!(denoise3(&flat, 5, 4), flat); + let mut dot = vec![0u8; 3 * 3 * 3]; + dot[(3 + 1) * 3] = 160; + let d = denoise3(&dot, 3, 3); + assert_eq!(d[(3 + 1) * 3], (160 * 4 + 8) >> 4); + assert_eq!(d[0], (160 + 8) >> 4); + } + + #[test] + fn the_class_rises_along_a_chain() { + assert!(Class::Captured < Class::Reconstructed && Class::Reconstructed < Class::Assistive && Class::Assistive < Class::Synthesized); + assert_eq!(Step::Convert.class(), Class::Reconstructed); + } + + #[test] + fn a_generator_envelope_generates() { + let env = |m: &str| { + let mut b = b"WAI1".to_vec(); + b.extend_from_slice(&(m.len() as u32).to_le_bytes()); + b.extend_from_slice(m.as_bytes()); + b.extend_from_slice(&0u32.to_le_bytes()); + b + }; + assert!(generates(&env(r#"{"wai":"1.0","media":"audio","intent":"create","model_requirement":{"capability":"wai.generate.spectral"}}"#))); + assert!(generates(&env(r#"{"wai":"1.0","media":"image","intent":"perceptual","model_requirement":{"capability":"wai.semantic.clip"}}"#))); + assert!(!generates(&env(r#"{"wai":"1.0","media":"image","intent":"replicate","model_requirement":{"capability":"wai.image.png"}}"#))); + assert!(!generates(b"not an envelope")); + } +} diff --git a/wai/wai-rs/src/synthesis/corpus.rs b/wai/wai-rs/src/synthesis/corpus.rs new file mode 100644 index 000000000..a84eb8d71 --- /dev/null +++ b/wai/wai-rs/src/synthesis/corpus.rs @@ -0,0 +1,1088 @@ +//! The conformance corpus of extensions/synthesis.md §13: `live-conformance/synthesis/`. +//! +//! [`generate`] writes it from fixed seeds: every key is SHA-256 of this corpus's label and +//! an index, every sample buffer is SHA-256 output of a label, every time and text is written +//! here, and Ed25519 signing is deterministic (RFC 8032), so the file is the same bytes on +//! every machine. Each case states the outcome it was written to have; the generator asserts +//! that this module reaches it before writing it, and that the corpus gives every reason of +//! §12. +//! +//! [`verify`] checks a corpus directory's files against this module. +//! `live-conformance/synthesis_verify.py` checks the same file from the extension's text, +//! sharing no code with this module. + +use super::*; +use crate::live_events::{kind, Caption, Event, Origin as Said, Policy as EventPolicy}; +use crate::live_room::{media, Caps, Descriptor, Grant, Limits, Recording, Review as RoomReview, Role, NO_CEILING}; +use serde_json::{json, Map, Value}; + +const T0_US: u64 = 1_792_108_800_000_000; +const T0_S: u64 = T0_US / 1_000_000; +const S: u64 = 1_000_000; +const ISSUED: &str = "2026-10-21T09:00:00Z"; + +const CAMERA: u8 = 0; +const CODEC: u8 = 1; +const EDITOR: u8 = 2; +const GENERATOR: u8 = 3; +const MARKER: u8 = 4; +const DETECTOR: u8 = 5; +const REVIEWER: u8 = 6; +const DEPLOYER: u8 = 7; +const PLAYER: u8 = 8; +const AUTHORITY: u8 = 9; +const CLOCK: u8 = 10; +const AI_HOST: u8 = 11; +const VIEWER: u8 = 12; + +fn sha(parts: &[&[u8]]) -> [u8; 32] { + sha256(parts) +} + +fn key(i: u8) -> SigningKey { + SigningKey::from_bytes(&sha(&[b"wai:synthesis/corpus/key\x00", &[i]])) +} + +fn pk(i: u8) -> [u8; 32] { + key(i).verifying_key().to_bytes() +} + +fn label(s: &str) -> [u8; 32] { + sha(&[b"wai:synthesis/corpus/label\x00", s.as_bytes()]) +} + +/// `n` bytes from a label: SHA-256 of the label and a counter, concatenated. +fn bytes(s: &str, n: usize) -> Vec { + (0u32..).flat_map(|i| sha(&[s.as_bytes(), &i.to_le_bytes()])).take(n).collect() +} + +fn text(v: &Value) -> String { + jcs(v).expect("a record in the subset") +} + +/// A `WAI1` envelope (SPEC §2) of this manifest and payload. +fn envelope(manifest: &str, payload: &[u8]) -> Vec { + let mut b = b"WAI1".to_vec(); + b.extend_from_slice(&(manifest.len() as u32).to_le_bytes()); + b.extend_from_slice(manifest.as_bytes()); + b.extend_from_slice(&(payload.len() as u32).to_le_bytes()); + b.extend_from_slice(payload); + b +} + +fn manifest(media: &str, intent: &str, cap: &str) -> String { + format!(r#"{{"wai":"1.0","media":"{media}","intent":"{intent}","model_requirement":{{"capability":"{cap}"}}}}"#) +} + +// ---- the objects and their samples ---- + +const W: usize = 4; +const H: usize = 3; + +struct Objects { + photo: Vec, + coded: Vec, + upscaled: Vec, + denoised: Vec, + sr: Vec, + video: Vec, + spectral: Vec, + inpainted: Vec, + composite: Vec, + transcode: Vec, + /// The canonical buffers: the photo's, the codec's reconstruction of it, and its two + /// pinned derivations; the generated video's first frame. + photo_px: Vec, + coded_px: Vec, + up_px: Vec, + dn_px: Vec, + video_px: Vec, +} + +fn objects() -> Objects { + let photo_px = bytes("photo", W * H * 3); + // The codec's reconstruction: the photo with its low bits cleared. + let coded_px: Vec = photo_px.iter().map(|b| b & 0xF8).collect(); + let up_px = upscale2(&coded_px, W, H); + let dn_px = denoise3(&coded_px, W, H); + let png = |s: &str| envelope(&manifest("image", "replicate", "wai.image.png"), &bytes(s, 48)); + Objects { + photo: png("photo envelope"), + coded: envelope(&manifest("image", "replicate", "wai.neural.int_hyper"), &bytes("coded", 40)), + upscaled: png("upscaled envelope"), + denoised: png("denoised envelope"), + sr: png("float super-resolution envelope"), + video: envelope(&manifest("video", "perceptual", "wai.video.av1"), &bytes("generated video", 64)), + spectral: envelope(&manifest("audio", "create", "wai.generate.spectral"), br#"{"form":0}"#), + inpainted: png("inpainted envelope"), + composite: png("composite envelope"), + transcode: envelope(&manifest("video", "perceptual", "wai.video.hevc"), &bytes("transcoded video", 64)), + photo_px, + coded_px, + up_px, + dn_px, + video_px: bytes("generated video frame", W * H * 3), + } +} + +fn ch(env: &[u8]) -> [u8; 32] { + crate::jwp_hash::content_hash(env) +} + +// ---- records ---- + +fn object_subject(env: &[u8]) -> Value { + json!({"type": "object", "content_hash": hex(&ch(env))}) +} + +#[allow(clippy::too_many_arguments)] +fn claim_body(subject: Value, media: &str, class: &str, step: &str, operation: &str, source: &str, models: Value, oversight: Value, inputs: Value, evidence: Value) -> Value { + json!({ + "kind": "wai-origin", + "subject": subject, + "media": media, + "class": class, + "step": step, + "operation": operation, + "record": null, + "source": source, + "models": models, + "oversight": oversight, + "inputs": inputs, + "evidence": evidence, + "issued_at": ISSUED, + }) +} + +/// The input entry naming a held claim (§3.1). +fn input_of(c: &Value) -> Value { + json!({ + "content_hash": c["subject"]["content_hash"], + "origin": hex(&link_hash(c)), + "class": c["class"], + "source": c["source"], + }) +} + +fn trained(s: &str) -> Value { + json!({"digest": hex(&label(s)), "type": "trained"}) +} + +fn evidence(tier: &str, derivation: &str, input: &[u8], output: &[u8]) -> Value { + json!({ + "tier": tier, + "derivation": derivation, + "params": {}, + "width": W, + "height": H, + "input_samples": hex(&sha(&[input])), + "output_samples": hex(&sha(&[output])), + }) +} + +/// The claims of the corpus's chain and of its generated objects, each a body before signing. +struct Bodies { + photo: Value, + coded: Value, + up: Value, + dn: Value, + sr: Value, + video: Value, + spectral: Value, +} + +fn signed(body: Value, who: u8) -> Value { + sign(body, &key(who)) +} + +fn bodies(o: &Objects) -> Bodies { + let photo = claim_body(object_subject(&o.photo), "image", "captured", "capture", "capture", "digitalCapture", json!([]), Value::Null, json!([]), Value::Null); + let photo_s = signed(photo.clone(), CAMERA); + let coded = + claim_body(object_subject(&o.coded), "image", "reconstructed", "convert", "wai.neural.int_hyper", "digitalCapture", json!([]), Value::Null, json!([input_of(&photo_s)]), Value::Null); + let coded_s = signed(coded.clone(), CODEC); + let up = claim_body( + object_subject(&o.upscaled), + "image", + "assistive", + "assist", + UPSCALE2, + "algorithmicallyEnhanced", + json!([]), + Value::Null, + json!([input_of(&coded_s)]), + evidence("replicate", UPSCALE2, &o.coded_px, &o.up_px), + ); + let dn = claim_body( + object_subject(&o.denoised), + "image", + "assistive", + "assist", + DENOISE3, + "algorithmicallyEnhanced", + json!([]), + Value::Null, + json!([input_of(&coded_s)]), + evidence("replicate", DENOISE3, &o.coded_px, &o.dn_px), + ); + let sr = claim_body( + object_subject(&o.sr), + "image", + "assistive", + "assist", + "super-resolution", + "algorithmicallyEnhanced", + json!([trained("float super-resolution model")]), + Value::Null, + json!([input_of(&coded_s)]), + json!({ + "tier": "attest", + "derivation": "super-resolution", + "params": {"scale": 2}, + "width": W, + "height": H, + "input_samples": hex(&sha(&[&o.coded_px])), + "output_samples": hex(&label("float super-resolution output")), + }), + ); + let video = claim_body( + object_subject(&o.video), + "video", + "synthesized", + "generate", + "video-generation", + "trainedAlgorithmicMedia", + json!([trained("video model")]), + json!("fully_autonomous"), + json!([]), + Value::Null, + ); + let spectral = claim_body( + object_subject(&o.spectral), + "audio", + "synthesized", + "generate", + "wai.generate.spectral", + "algorithmicMedia", + json!([{"digest": hex(&label("spectral model bundle")), "type": "algorithmic"}]), + json!("prompt_guided"), + json!([]), + Value::Null, + ); + Bodies { photo, coded, up, dn, sr, video, spectral } +} + +const SCHEME_DOC: &[u8] = b"wai-mark:conformance names no watermark algorithm (synthesis.md section 7.4)"; + +fn marking(c: &Value, payload: Option<[u8; 8]>) -> Value { + let p = payload.unwrap_or_else(|| read_origin(&text(c)).expect("a corpus claim").mark_payload()); + signed( + json!({ + "kind": "wai-marking", + "subject": c["subject"]["content_hash"], + "origin": hex(&link_hash(c)), + "scheme": CONFORMANCE_SCHEME, + "scheme_digest": hex(&sha(&[SCHEME_DOC])), + "payload": hex(&p), + "issued_at": ISSUED, + }), + MARKER, + ) +} + +fn detection(subject: &Value, payload: Option<[u8; 8]>, scheme_doc: &[u8]) -> Value { + signed( + json!({ + "kind": "wai-detection", + "subject": subject, + "scheme": CONFORMANCE_SCHEME, + "scheme_digest": hex(&sha(&[scheme_doc])), + "payload": payload.map(|p| hex(&p)), + "issued_at": ISSUED, + }), + DETECTOR, + ) +} + +fn payload_of(c: &Value) -> [u8; 8] { + read_origin(&text(c)).expect("a corpus claim").mark_payload() +} + +fn review(c: &Value, reconstruction: &[u8; 32], tier: &str, verdict: &str) -> Value { + signed( + json!({ + "kind": "wai-review", + "subject": c["subject"]["content_hash"], + "origin": hex(&link_hash(c)), + "tier": tier, + "reconstruction": hex(reconstruction), + "decoder": "builtin:wai@0.3.0", + "variant": null, + "scope": "rendering", + "verdict": verdict, + "notes": if verdict == "approved" { Value::Null } else { json!(hex(&label("review notes"))) }, + "role": null, + "issued_at": ISSUED, + }), + REVIEWER, + ) +} + +fn icon(name: &str) -> [u8; 32] { + let h = EU_ICONS.iter().find(|(n, _)| *n == name).expect("a pinned icon").1; + unhex(h).expect("hex").try_into().expect("32 bytes") +} + +fn label_policy(interval_us: Option, grace_us: u64) -> Value { + signed( + json!({ + "kind": "wai-label-policy", + "classes": ["synthesized"], + "visual": hex(&icon("LABEL_AI GENERATED_black.svg")), + "audio": hex(&label("audible disclaimer")), + "interval_us": interval_us, + "grace_us": grace_us, + "issued_at": ISSUED, + }), + DEPLOYER, + ) +} + +/// A player's log being written (§10.3). +struct Log { + session: [u8; 32], + entries: Vec, +} + +impl Log { + fn new() -> Log { + Log { session: label("player session"), entries: vec![] } + } + + fn push(&mut self, at: u64, entry: Value) -> &mut Log { + let prev = self.entries.last().map(|e| json!(hex(&link_hash(e)))).unwrap_or(Value::Null); + let e = signed( + json!({"kind": "wai-player", "session": hex(&self.session), "seq": self.entries.len(), "prev": prev, "at": at, "entry": entry}), + PLAYER, + ); + self.entries.push(e); + self + } + + fn texts(&self) -> Vec { + self.entries.iter().map(text).collect() + } +} + +fn present(c: &Value, tier: &str, output: Option<[u8; 32]>) -> Value { + json!({ + "type": "present", + "subject": c["subject"]["content_hash"], + "origin": hex(&link_hash(c)), + "class": c["class"], + "media": c["media"], + "tier": tier, + "decoder": "builtin:wai@0.3.0", + "variant": null, + "output": output.map(|o| hex(&o)), + }) +} + +fn shown(asset: [u8; 32], trigger: &str) -> Value { + json!({"type": "shown", "label": "visual", "asset": hex(&asset), "trigger": trigger}) +} + +fn resume() -> Value { + json!({"type": "resume", "after": "ad-break"}) +} + +// ---- the room ---- + +const MODEL: &str = "voice agent model"; + +/// A room with an AI host whose grant names its model (live-rooms §4.4), a viewer, and the +/// host publishing speech. `said`: the origin its chat states. +fn ai_room(said: Option) -> RoomInput { + let d = Descriptor { + authority: pk(AUTHORITY), + clock: pk(CLOCK), + t0_us: T0_US, + tick_us: S, + origin: String::new(), + limits: Limits { max_present: 8, max_publishers: 2, reval_s: 0, ceiling: EventPolicy::default() }, + roles: vec![ + Role { name: "host".into(), caps: Caps { publish: 1, subscribe: true, max_layer: 0, powers: 0 }, events: bits(&[kind::CHAT, kind::CAPTION]) }, + Role { name: "audience".into(), caps: Caps { publish: 0, subscribe: true, max_layer: 0, powers: 0 }, events: bits(&[kind::CHAT, kind::REACTION]) }, + ], + slots: vec![], + profiles: vec![room::registered_profile("speech").expect("a registered profile")], + review: RoomReview { kinds: [0; 32], deadline_ms: 0, fallback: 0 }, + recording: Recording { mode: 0, retention_days: 0, commit_interval_ms: 0, jitter_ms: 0, regions: vec![] }, + sig: [0; 64], + } + .sign(&key(AUTHORITY)); + let id = d.room_id(); + let grant = |who: u8, role: usize, profiles: Vec, attrs: Vec<(String, String)>| { + let r = &d.roles[role]; + let kinds: Vec = (0..=255u8).filter(|&k| r.events[(k / 8) as usize] >> (k % 8) & 1 == 1).collect(); + Grant { + room: id, + principal: pk(who), + prev: [0; 32], + effective: 0, + nbf: T0_S - 3_600, + exp: T0_S + 86_400, + role: role as u8, + caps: r.caps, + precedence: 0, + once: [0; 16], + viewer: [0; 16], + policy: EventPolicy::with_kinds(&kinds), + joules_ceiling: NO_CEILING, + funds_ceiling: NO_CEILING, + currency: [0; 3], + profiles, + origins: vec!["https://relay.example".into()], + attrs, + sig: [0; 64], + } + .sign(&key(AUTHORITY)) + }; + let host = grant(AI_HOST, 0, vec![0], vec![("ai".into(), hex(&label(MODEL)))]); + let viewer = grant(VIEWER, 1, vec![], vec![]); + let mut chains: Vec<(u8, Vec>, u64, [u8; 32])> = vec![]; + let mut emit = |who: u8, t: u64, p: Payload| { + let i = match chains.iter().position(|c| c.0 == who) { + Some(i) => i, + None => { + chains.push((who, vec![], 0, [0; 32])); + chains.len() - 1 + } + }; + let c = &mut chains[i]; + let e = Event::sign(&key(who), id, c.2, c.3, t, None, p.kind(), p.encode()); + c.2 += 1; + c.3 = e.digest(); + c.1.push(e.to_bytes()); + }; + let life = |d: Detail, subject: u8| Payload::Lifecycle { kind: d.kind(), subject: pk(subject), detail: d.encode() }; + emit(CLOCK, 0, life(Detail::Join { grant: host.digest() }, AI_HOST)); + emit(CLOCK, 0, life(Detail::Join { grant: viewer.digest() }, VIEWER)); + emit(CLOCK, S, life(Detail::Publish { slot: String::new(), media: media::AUDIO, profile: 0, layers: 1 }, AI_HOST)); + emit(VIEWER, 2 * S, Payload::Chat { text: "Hello?".into(), origin: None }); + emit(AI_HOST, 3 * S, Payload::Chat { text: "Hello. I am an automated assistant.".into(), origin: said }); + emit(AI_HOST, 4 * S, Payload::Caption(Caption { duration_ms: 2_000, origin: 2, model: Some(label(MODEL)), lang: "en".into(), text: "Hello.".into() })); + RoomInput { descriptor: d.to_bytes(), grants: vec![host.to_bytes(), viewer.to_bytes()], head: None, chains: chains.into_iter().map(|c| c.1).collect() } +} + +fn bits(ks: &[u8]) -> [u8; 32] { + let mut b = [0u8; 32]; + for &k in ks { + b[(k / 8) as usize] |= 1 << (k % 8); + } + b +} + +fn ai_track_claim(r: &RoomInput) -> Value { + let id = Descriptor::parse(&r.descriptor).expect("the corpus room").room_id(); + let track = track_digest(&id, &hex(&pk(AI_HOST)), "audio", 0); + signed( + claim_body( + json!({"type": "track", "track": hex(&track), "from_group": 0}), + "audio", + "synthesized", + "generate", + "speech-synthesis", + "trainedAlgorithmicMedia", + json!([trained(MODEL)]), + json!("fully_autonomous"), + json!([]), + Value::Null, + ), + AI_HOST, + ) +} + +// ---- vectors ---- + +/// What a vector was written to do. +#[derive(Clone, Debug)] +enum Intent { + Accept, + Refuse(At, Reason), +} + +struct Vector { + name: &'static str, + about: &'static str, + case: Case, + intent: Intent, +} + +fn case(claims: &[&Value]) -> Case { + Case { claims: claims.iter().map(|c| text(c)).collect(), ..Default::default() } +} + +fn decoded(pairs: &[(&[u8], &[u8])]) -> Vec<([u8; 32], Vec)> { + pairs.iter().map(|(env, px)| (ch(env), px.to_vec())).collect() +} + +fn with(mut body: Value, path: &[&str], v: Value) -> Value { + let mut at = &mut body; + for p in &path[..path.len() - 1] { + at = at.get_mut(*p).expect("a member"); + } + at[path[path.len() - 1]] = v; + body +} + +fn vectors() -> Vec { + let o = objects(); + let b = bodies(&o); + let photo = signed(b.photo.clone(), CAMERA); + let coded = signed(b.coded.clone(), CODEC); + let up = signed(b.up.clone(), EDITOR); + let dn = signed(b.dn.clone(), EDITOR); + let sr = signed(b.sr.clone(), EDITOR); + let video = signed(b.video.clone(), GENERATOR); + let spectral = signed(b.spectral.clone(), GENERATOR); + let inpainted = signed( + claim_body( + object_subject(&o.inpainted), + "image", + "synthesized", + "generate", + "inpainting", + "compositeWithTrainedAlgorithmicMedia", + json!([trained("image model")]), + json!("human_validated"), + json!([input_of(&photo)]), + Value::Null, + ), + GENERATOR, + ); + let composite = signed( + claim_body( + object_subject(&o.composite), + "image", + "synthesized", + "convert", + "wai.derive.overlay_composition", + "compositeSynthetic", + json!([]), + Value::Null, + json!([input_of(&photo), input_of(&video)]), + Value::Null, + ), + EDITOR, + ); + let transcode = signed(transcode_body(&o, &video), CODEC); + let chain_decoded = decoded(&[(&o.coded, &o.coded_px), (&o.upscaled, &o.up_px), (&o.denoised, &o.dn_px)]); + let chain = || Case { + claims: [&photo, &coded, &up, &dn].iter().map(|c| text(c)).collect(), + objects: vec![o.photo.clone(), o.coded.clone(), o.upscaled.clone(), o.denoised.clone()], + decoded: chain_decoded.clone(), + policy: Policy { dual_layer_required: false, require_proof: true }, + ..Default::default() + }; + let generated = || Case { + claims: [&photo, &video, &spectral, &inpainted, &composite, &transcode].iter().map(|c| text(c)).collect(), + markings: [marking(&video, None), marking(&spectral, None), marking(&inpainted, None), marking(&composite, None), marking(&transcode, None)] + .iter() + .map(text) + .collect(), + detections: vec![ + text(&detection(&video["subject"]["content_hash"], Some(payload_of(&video)), SCHEME_DOC)), + text(&detection(&photo["subject"]["content_hash"], None, SCHEME_DOC)), + ], + objects: vec![o.photo.clone(), o.video.clone(), o.spectral.clone(), o.inpainted.clone(), o.composite.clone(), o.transcode.clone()], + policy: Policy { dual_layer_required: true, require_proof: false }, + ..Default::default() + }; + let video_out = sha(&[&o.video_px]); + let certified = |log: &Log, rv: &Value, lp: Option<&Value>| Case { + claims: vec![text(&video)], + reviews: vec![text(rv)], + label_policy: lp.map(text), + player: log.texts(), + objects: vec![o.video.clone()], + decoded: decoded(&[(&o.video, &o.video_px)]), + ..Default::default() + }; + let approved = review(&video, &video_out, "decode-equivalence", "approved"); + let gen_icon = icon("LABEL_AI GENERATED_black.svg"); + // The certified session: first exposure labelled, a review presented, an ad break, the + // label again on return, and a presentation within the interval. + let session = |tier: &str, out: Option<[u8; 32]>, rv: &Value| { + let mut l = Log::new(); + l.push(0, shown(gen_icon, "first-exposure")) + .push(0, present(&video, tier, out)) + .push(S, json!({"type": "review", "subject": video["subject"]["content_hash"], "review": hex(&link_hash(rv))})) + .push(20 * S, resume()) + .push(20 * S + 200_000, shown(gen_icon, "resume")) + .push(20 * S + 200_000, present(&video, tier, out)) + .push(40 * S, present(&video, tier, out)); + l + }; + + let mut out = vec![ + Vector { + name: "chain", + about: "a capture, a neural codec's reconstruction of it, and two pinned integer derivations of that reconstruction, each recomputed from its input's decoded samples and proved (§3, §4)", + case: chain(), + intent: Intent::Accept, + }, + Vector { + name: "assistive_asserted", + about: "a float super-resolution step that states the assistive class with attest evidence: asserted, not proved, under a policy that accepts assertions (§4.4)", + case: Case { claims: [&photo, &coded, &sr].iter().map(|c| text(c)).collect(), ..Default::default() }, + intent: Intent::Accept, + }, + Vector { + name: "generated", + about: "a generated video, a pinned algorithmic audio generation, an inpainted capture reviewed by a person, a composite of a capture and the generated video, and a transcode of it; each synthesized object marked in both layers, a detection agreeing with the video's claim, and one finding no mark on the capture (§5, §6, §7)", + case: generated(), + intent: Intent::Accept, + }, + Vector { + name: "ai_room", + about: "a room whose AI host's grant names its model: its chat and caption state that model, and its published speech track has a synthesized track claim naming it (§8)", + case: Case { claims: vec![text(&ai_track_claim(&ai_room(None)))], room: Some(ai_room(Some(Said { origin: 2, model: Some(label(MODEL)) }))), ..Default::default() }, + intent: Intent::Accept, + }, + Vector { + name: "certified", + about: "a review of a generated video at decode-equivalence, presented by a player whose decode path holds that tier and whose output is the reconstruction reviewed; the label shown at first exposure, after an ad break and within the interval (§9, §10)", + case: certified(&session("decode-equivalence", Some(video_out), &approved), &approved, Some(&label_policy(Some(30 * S), 500_000))), + intent: Intent::Accept, + }, + ]; + + // ---- refusals, each a control of one defect ---- + let mut refuse = |name: &'static str, about: &'static str, case: Case, at: At, reason: Reason| out.push(Vector { name, about, case, intent: Intent::Refuse(at, reason) }); + + let extra = with(photo.clone(), &["note"], json!("an extra member")); + refuse("record_malformed", "an origin claim with a member §3.1 does not list", case(&[&extra]), At::Claim(0), Reason::RecordMalformed); + let captured_with_input = signed(with(b.photo.clone(), &["inputs"], json!([input_of(&coded)])), CAMERA); + refuse( + "record_malformed_step", + "a capture that names an input: a capture has none (§3.2)", + case(&[&captured_with_input]), + At::Claim(0), + Reason::RecordMalformed, + ); + let mut flipped = photo.clone(); + let sig = flipped["sig"].as_str().expect("a signature").to_string(); + flipped["sig"] = json!(format!("{}{}", if &sig[..1] == "0" { "1" } else { "0" }, &sig[1..])); + refuse("bad_signature", "an origin claim whose signature has one digit changed", case(&[&flipped]), At::Claim(0), Reason::BadSignature); + let mut small = photo.clone(); + small["signer_pubkey"] = json!(format!("01{}", "00".repeat(31))); + refuse("bad_key", "an origin claim whose key is the identity point, of small order (SPEC §7 *Signatures* step 2)", case(&[&small]), At::Claim(0), Reason::BadKey); + let again = signed(with(b.photo.clone(), &["issued_at"], json!("2026-10-21T10:00:00Z")), CODEC); + refuse("claim_conflict", "two origin claims on one object", case(&[&photo, &again]), At::Claim(1), Reason::ClaimConflict); + let understated = signed(with(transcode_body(&o, &video), &["class"], json!("reconstructed")), CODEC); + refuse( + "class_mismatch", + "a transcode of a generated video that states the class reconstructed: a conversion keeps its input's class (§3.2)", + case(&[&video, &understated]), + At::Claim(1), + Reason::ClassMismatch, + ); + let wrong_source = signed(with(b.coded.clone(), &["source"], json!("digitalCreation")), CODEC); + refuse( + "source_mismatch", + "a codec's reconstruction of a capture that states digitalCreation: a conversion keeps its input's source (§5)", + case(&[&photo, &wrong_source]), + At::Claim(1), + Reason::SourceMismatch, + ); + let restated = with(input_of(&photo), &["class"], json!("reconstructed")); + let bad_input = signed(with(with(b.coded.clone(), &["inputs"], json!([restated])), &["class"], json!("reconstructed")), CODEC); + refuse( + "input_mismatch", + "a reconstruction that restates its input's class as reconstructed where the input's own claim says captured (§3.3)", + case(&[&photo, &bad_input]), + At::Claim(1), + Reason::InputMismatch, + ); + let labelled_captured = signed(with(b.photo.clone(), &["subject"], object_subject(&o.spectral)), CAMERA); + refuse( + "class_understated", + "a synthesized object labelled captured: the object is a generator's envelope (wai.generate.spectral, intent create), and its claim is a capture (§3.3)", + Case { claims: vec![text(&labelled_captured)], objects: vec![o.spectral.clone()], ..Default::default() }, + At::Claim(0), + Reason::ClassUnderstated, + ); + let unpinned = signed(with(b.up.clone(), &["evidence", "derivation"], json!("wai-synthesis:upscale3")), EDITOR); + refuse( + "evidence_invalid", + "replicate evidence naming a derivation §4 does not pin", + Case { claims: vec![text(&photo), text(&coded), text(&unpinned)], ..Default::default() }, + At::Claim(2), + Reason::EvidenceInvalid, + ); + let wrong_input = signed(with(b.up.clone(), &["evidence", "input_samples"], json!(hex(&sha(&[&o.photo_px])))), EDITOR); + refuse( + "input_samples_mismatch", + "an upscale whose evidence names the capture's samples, not those its input decodes to (§4.4)", + Case { claims: vec![text(&photo), text(&coded), text(&wrong_input)], decoded: chain_decoded.clone(), ..Default::default() }, + At::Claim(2), + Reason::InputSamplesMismatch, + ); + let mut sharpened = o.up_px.clone(); + sharpened[7] ^= 0x40; + let not_recomputed = signed(with(b.up.clone(), &["evidence", "output_samples"], json!(hex(&sha(&[&sharpened])))), EDITOR); + refuse( + "assistive_recompute_mismatch", + "an assistive claim whose recomputation differs: the delivered upscale has one sample altered after the derivation, and its evidence states the altered samples (§4.4)", + Case { + claims: vec![text(&photo), text(&coded), text(¬_recomputed)], + decoded: decoded(&[(&o.coded, &o.coded_px), (&o.upscaled, &sharpened)]), + ..Default::default() + }, + At::Claim(2), + Reason::AssistiveRecomputeMismatch, + ); + refuse( + "samples_mismatch", + "an upscale whose evidence recomputes, delivered as an object that decodes to other samples (§4.4)", + Case { + claims: vec![text(&photo), text(&coded), text(&up)], + decoded: decoded(&[(&o.coded, &o.coded_px), (&o.upscaled, &sharpened)]), + ..Default::default() + }, + At::Claim(2), + Reason::SamplesMismatch, + ); + refuse( + "assistive_not_proved", + "a float super-resolution step with attest evidence, under a policy that requires every assistive claim to be proved (§4.4, §11.1)", + Case { claims: [&photo, &coded, &sr].iter().map(|c| text(c)).collect(), policy: Policy { require_proof: true, ..Default::default() }, ..Default::default() }, + At::Claim(2), + Reason::AssistiveNotProved, + ); + refuse( + "claim_unknown", + "a marking whose origin claim the verifier is not given (§7.3)", + Case { claims: vec![text(&photo)], markings: vec![text(&marking(&video, None))], ..Default::default() }, + At::Marking(0), + Reason::ClaimUnknown, + ); + let mut lowered = payload_of(&video); + lowered[0] = Class::Captured.code(); + refuse( + "marking_mismatch", + "a marking whose payload carries the class captured for a synthesized claim (§7.2)", + Case { claims: vec![text(&video)], markings: vec![text(&marking(&video, Some(lowered)))], ..Default::default() }, + At::Marking(0), + Reason::MarkingMismatch, + ); + refuse( + "detection_mismatch", + "a detection under another scheme definition than the one the marking names (§7.3)", + Case { + claims: vec![text(&video)], + markings: vec![text(&marking(&video, None))], + detections: vec![text(&detection(&video["subject"]["content_hash"], Some(payload_of(&video)), b"another scheme definition"))], + ..Default::default() + }, + At::Detection(0), + Reason::DetectionMismatch, + ); + refuse( + "watermark_absent", + "a marked object in which the detector finds no mark: the second layer was stripped (§7.3)", + Case { + claims: vec![text(&video)], + markings: vec![text(&marking(&video, None))], + detections: vec![text(&detection(&video["subject"]["content_hash"], None, SCHEME_DOC))], + ..Default::default() + }, + At::Detection(0), + Reason::WatermarkAbsent, + ); + let mut foreign = payload_of(&video); + foreign[0] = Class::Synthesized.code(); + refuse( + "layers_disagree", + "the two marking layers disagree: the photo's claim says captured, and the detector reads from it a synthesized mark that points at another claim (§7.3)", + Case { claims: vec![text(&photo)], detections: vec![text(&detection(&photo["subject"]["content_hash"], Some(foreign), SCHEME_DOC))], ..Default::default() }, + At::Detection(0), + Reason::LayersDisagree, + ); + refuse( + "layer_missing", + "a synthesized object with no marking, under a policy that requires both layers (§7.1)", + Case { claims: vec![text(&video)], policy: Policy { dual_layer_required: true, ..Default::default() }, ..Default::default() }, + At::Claim(0), + Reason::LayerMissing, + ); + let r = ai_room(Some(Said { origin: 2, model: Some(label(MODEL)) })); + refuse( + "ai_track_unlabelled", + "an AI participant publishes speech with no synthesized track claim for it (§8.2)", + Case { room: Some(r), ..Default::default() }, + At::Room(room::At::Record { chain: 0, record: 2 }), + Reason::AiTrackUnlabelled, + ); + let r = ai_room(None); + refuse( + "ai_unmarked", + "an AI participant's chat that states no origin: the room refuses it (live-rooms §9.2 step 6)", + Case { claims: vec![text(&ai_track_claim(&r))], room: Some(r), ..Default::default() }, + At::Room(room::At::Record { chain: 2, record: 0 }), + Reason::Room(room::Reason::AiUnmarked), + ); + let mut broken = session("decode-equivalence", Some(video_out), &approved); + broken.entries[2] = signed(with(broken.entries[2].clone(), &["seq"], json!(3)), PLAYER); + refuse("player_chain_broken", "a player's log whose third entry claims seq 3 (§10.3)", certified(&broken, &approved, None), At::Player(2), Reason::PlayerChainBroken); + let mut l = Log::new(); + l.push(0, with(present(&video, "decode-equivalence", Some(video_out)), &["class"], json!("assistive"))); + refuse("present_mismatch", "a presentation that states the class assistive for a synthesized object (§10.3)", certified(&l, &approved, None), At::Player(0), Reason::PresentMismatch); + let mut l = Log::new(); + l.push(0, present(&video, "decode-equivalence", Some(label("another output")))); + refuse( + "present_output_irreproducible", + "a presentation whose stated output is not what the object decodes to (§9.2)", + certified(&l, &approved, None), + At::Player(0), + Reason::PresentOutputIrreproducible, + ); + let mut l = Log::new(); + l.push(0, present(&photo, "decode-equivalence", None)) + .push(S, json!({"type": "review", "subject": video["subject"]["content_hash"], "review": hex(&link_hash(&approved))})); + refuse( + "review_subject_mismatch", + "a review presented for an object the player has not presented (§9.2)", + Case { claims: vec![text(&video), text(&photo)], reviews: vec![text(&approved)], player: l.texts(), ..Default::default() }, + At::Player(1), + Reason::ReviewSubjectMismatch, + ); + let rejected = review(&video, &video_out, "decode-equivalence", "rejected"); + refuse( + "review_rejected", + "a rejected review presented as a review (§9.2)", + certified(&session("decode-equivalence", Some(video_out), &rejected), &rejected, None), + At::Player(2), + Reason::ReviewRejected, + ); + refuse( + "review_path_not_exact", + "a review presented on a non-exact path: the player decoded through a float path, tier none, and presents a review made at decode-equivalence (§9.2)", + certified(&session("none", Some(video_out), &approved), &approved, None), + At::Player(2), + Reason::ReviewPathNotExact, + ); + let other = review(&video, &label("another reconstruction"), "decode-equivalence", "approved"); + refuse( + "review_reconstruction_mismatch", + "a review over another reconstruction than the one the player presented (§9.2)", + certified(&session("decode-equivalence", Some(video_out), &other), &other, None), + At::Player(2), + Reason::ReviewReconstructionMismatch, + ); + let mut l = Log::new(); + l.push(0, shown(icon("LABEL_AI MODIFIED_black.svg"), "first-exposure")).push(0, present(&video, "decode-equivalence", Some(video_out))); + refuse( + "label_asset_mismatch", + "a label shown with another icon than the policy's (§10.4)", + certified(&l, &approved, Some(&label_policy(None, 500_000))), + At::Player(0), + Reason::LabelAssetMismatch, + ); + let mut l = Log::new(); + l.push(0, present(&video, "decode-equivalence", Some(video_out))).push(5 * S, shown(gen_icon, "first-exposure")); + refuse( + "label_not_shown", + "a label not shown at first exposure: the player's log shows the generated video presented at 0 s and the label first at 5 s, past the half-second grace (§10.4)", + certified(&l, &approved, Some(&label_policy(None, 500_000))), + At::Player(0), + Reason::LabelNotShown, + ); + let mut l = Log::new(); + l.push(0, shown(gen_icon, "first-exposure")).push(0, present(&video, "decode-equivalence", Some(video_out))).push(20 * S, resume()).push(20 * S, present(&video, "decode-equivalence", Some(video_out))); + refuse( + "label_not_shown_resume", + "no label after an ad break: presentation resumes at 20 s with no label shown since (§10.4)", + certified(&l, &approved, Some(&label_policy(None, 500_000))), + At::Player(3), + Reason::LabelNotShown, + ); + let mut l = Log::new(); + l.push(0, shown(gen_icon, "first-exposure")).push(0, present(&video, "decode-equivalence", Some(video_out))).push(45 * S, present(&video, "decode-equivalence", Some(video_out))); + refuse( + "label_not_shown_interval", + "no label within the policy's 30 s interval: the label last shown at 0 s, the video still presented at 45 s (§10.4)", + certified(&l, &approved, Some(&label_policy(Some(30 * S), 500_000))), + At::Player(2), + Reason::LabelNotShown, + ); + out +} + +fn transcode_body(o: &Objects, video: &Value) -> Value { + claim_body( + object_subject(&o.transcode), + "video", + "synthesized", + "convert", + "wai.video.hevc", + "trainedAlgorithmicMedia", + json!([]), + Value::Null, + json!([input_of(video)]), + Value::Null, + ) +} + +// ---- the file ---- + +fn case_json(c: &Case) -> Map { + let mut m = Map::new(); + m.insert( + "policy".into(), + json!({ + "dual_layer": if c.policy.dual_layer_required { "required" } else { "when-given" }, + "assistive": if c.policy.require_proof { "require-proof" } else { "accept-asserted" }, + }), + ); + m.insert("claims".into(), json!(c.claims)); + m.insert("markings".into(), json!(c.markings)); + m.insert("detections".into(), json!(c.detections)); + m.insert("reviews".into(), json!(c.reviews)); + m.insert("label_policy".into(), json!(c.label_policy)); + m.insert("player".into(), json!(c.player)); + m.insert("objects".into(), json!(c.objects.iter().map(|b| hex(b)).collect::>())); + m.insert("decoded".into(), json!(c.decoded.iter().map(|(h, b)| json!({"object": hex(h), "samples": hex(b)})).collect::>())); + m.insert( + "room".into(), + match &c.room { + None => Value::Null, + Some(r) => json!({ + "descriptor": hex(&r.descriptor), + "grants": r.grants.iter().map(|g| hex(g)).collect::>(), + "key_log": r.head, + "chains": r.chains.iter().map(|ch| ch.iter().map(|e| hex(e)).collect::>()).collect::>(), + }), + }, + ); + m +} + +fn outcome_json(o: &Outcome) -> Map { + let mut m = Map::new(); + match o { + Outcome::Refuse { at, reason } => { + m.insert("outcome".into(), json!("refuse")); + m.insert("reason".into(), json!(reason.code())); + m.insert("at".into(), at.to_json()); + } + Outcome::Accept(r) => { + m.insert("outcome".into(), json!("accept")); + m.insert( + "statements".into(), + json!({ + "claims": r.claims, + "detections": r.detections, + "ai_tracks": r.ai_tracks, + "reviews_presented": r.reviews_presented, + "labels": r.labels, + "visual_icon": r.visual_icon, + "room": r.room, + }), + ); + } + } + m +} + +fn case_from_json(v: &Value) -> Option { + let strs = |k: &str| -> Option> { v[k].as_array()?.iter().map(|x| x.as_str().map(String::from)).collect() }; + let room = match &v["room"] { + Value::Null => None, + r => Some(RoomInput { + descriptor: unhex(r["descriptor"].as_str()?)?, + grants: r["grants"].as_array()?.iter().map(|g| unhex(g.as_str()?)).collect::>()?, + head: r["key_log"].as_str().map(String::from), + chains: r["chains"].as_array()?.iter().map(|c| c.as_array()?.iter().map(|e| unhex(e.as_str()?)).collect::>>()).collect::>()?, + }), + }; + Some(Case { + claims: strs("claims")?, + markings: strs("markings")?, + detections: strs("detections")?, + reviews: strs("reviews")?, + label_policy: v["label_policy"].as_str().map(String::from), + player: strs("player")?, + objects: v["objects"].as_array()?.iter().map(|x| unhex(x.as_str()?)).collect::>()?, + decoded: v["decoded"] + .as_array()? + .iter() + .map(|d| Some((unhex(d["object"].as_str()?)?.try_into().ok()?, unhex(d["samples"].as_str()?)?))) + .collect::>()?, + room, + policy: Policy { dual_layer_required: v["policy"]["dual_layer"] == "required", require_proof: v["policy"]["assistive"] == "require-proof" }, + }) +} + +/// The corpus: `vectors.json`. +pub fn generate() -> Vec<(String, String)> { + let vs = vectors(); + let mut seen = BTreeSet::new(); + let mut cases = Vec::new(); + for v in &vs { + let got = super::verify(&v.case); + match (&v.intent, &got) { + (Intent::Accept, Outcome::Accept(_)) => {} + (Intent::Refuse(at, r), Outcome::Refuse { at: a, reason }) if at == a && r == reason => { + seen.insert(*r); + } + _ => panic!("vector {} does not reach the outcome it was written to have: {got:?}", v.name), + } + let mut m = Map::new(); + m.insert("name".into(), json!(v.name)); + m.insert("about".into(), json!(v.about)); + m.extend(case_json(&v.case)); + m.extend(outcome_json(&got)); + cases.push(Value::Object(m)); + } + for r in Reason::ALL { + assert!(seen.contains(&r), "no vector of the corpus is refused with {r}"); + } + let doc = json!({ + "about": "the conformance corpus of extensions/synthesis.md §13, written by wai_synthesis_vectors gen", + "eu_icons": EU_ICONS.iter().map(|(n, d)| json!({"file": n, "sha256": d})).collect::>(), + "vectors": cases, + }); + vec![("vectors.json".into(), serde_json::to_string_pretty(&doc).expect("JSON") + "\n")] +} + +/// Check the corpus's files: every case gets the outcome and the statements it states. +pub fn verify(files: &BTreeMap) -> Vec { + let mut fails = Vec::new(); + let Some(doc) = files.get("vectors.json").and_then(|s| serde_json::from_str::(s).ok()) else { + return vec!["vectors.json does not read".into()]; + }; + for v in doc["vectors"].as_array().into_iter().flatten() { + let name = v["name"].as_str().unwrap_or("?"); + let Some(c) = case_from_json(v) else { + fails.push(format!("{name}: the case does not read")); + continue; + }; + let got = Value::Object(outcome_json(&verify_case(&c))); + let mut want = Map::new(); + for k in ["outcome", "reason", "at", "statements"] { + if let Some(x) = v.get(k) { + want.insert(k.into(), x.clone()); + } + } + if got != Value::Object(want) { + fails.push(format!("{name}: {got}")); + } + } + fails +} + +fn verify_case(c: &Case) -> Outcome { + super::verify(c) +} diff --git a/wai/wai-rs/tests/synthesis.rs b/wai/wai-rs/tests/synthesis.rs new file mode 100644 index 000000000..73354cfc0 --- /dev/null +++ b/wai/wai-rs/tests/synthesis.rs @@ -0,0 +1,71 @@ +//! `wai.media.synthesis` (extensions/synthesis.md): the committed corpus +//! `live-conformance/synthesis/` regenerates byte for byte from its fixed seeds, holds no +//! other file, every case in it gets the outcome it states, and the extension's §12 lists +//! exactly the reasons the reference reports. The generator itself asserts that the corpus +//! gives every one of them. +#![cfg(feature = "synthesis")] + +use std::collections::BTreeMap; +use std::path::PathBuf; +use wai::synthesis::{corpus, Reason}; + +fn wai_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..") +} + +fn committed() -> BTreeMap { + let dir = wai_dir().join("live-conformance/synthesis"); + std::fs::read_dir(&dir) + .expect("the committed corpus") + .flatten() + .map(|e| (e.file_name().to_string_lossy().into_owned(), e.path())) + .filter(|(n, _)| n != "README.md") + .map(|(n, p)| (n, std::fs::read_to_string(p).expect("a text file"))) + .collect() +} + +#[test] +fn the_committed_corpus_regenerates_from_its_seeds() { + let files = committed(); + let generated: BTreeMap = corpus::generate().into_iter().collect(); + let extra: Vec<&String> = files.keys().filter(|k| !generated.contains_key(*k)).collect(); + let missing: Vec<&String> = generated.keys().filter(|k| !files.contains_key(*k)).collect(); + let differ: Vec<&String> = generated.iter().filter(|(k, v)| files.get(*k).is_some_and(|f| f != *v)).map(|(k, _)| k).collect(); + assert!(extra.is_empty() && missing.is_empty() && differ.is_empty(), "extra {extra:?}, missing {missing:?}, differ {differ:?}: run `wai_synthesis_vectors gen`"); +} + +#[test] +fn every_case_gets_its_stated_outcome() { + let fails = corpus::verify(&committed()); + assert!(fails.is_empty(), "{}", fails.join("\n")); +} + +/// §12 of the extension lists exactly the codes `Reason::ALL` has, each once, in order. +#[test] +fn the_extension_lists_the_reasons() { + let text = std::fs::read_to_string(wai_dir().join("extensions/synthesis.md")).expect("the extension"); + let at = text.find("\n## 12. Reasons").expect("a §12"); + let end = text[at + 1..].find("\n## ").map_or(text.len(), |e| at + 1 + e); + let listed: Vec = + text[at..end].lines().filter(|l| l.starts_with("| `")).filter_map(|l| l.split('`').nth(1).map(String::from)).collect(); + let want: Vec = Reason::ALL.iter().map(|r| r.code().to_string()).collect(); + assert_eq!(listed, want, "§12's table and Reason::ALL differ"); +} + +/// §10.2's table pins exactly the icons `EU_ICONS` holds, each by its SHA-256, in order. +#[test] +fn the_extension_pins_the_icons() { + let text = std::fs::read_to_string(wai_dir().join("extensions/synthesis.md")).expect("the extension"); + let at = text.find("\n### 10.2 ").expect("a §10.2"); + let end = text[at + 1..].find("\n### ").map_or(text.len(), |e| at + 1 + e); + let pinned: Vec<(String, String)> = text[at..end] + .lines() + .filter(|l| l.starts_with("| `LABEL_AI")) + .filter_map(|l| { + let cells: Vec<&str> = l.split('`').collect(); + Some((cells.get(1)?.to_string(), cells.get(3)?.to_string())) + }) + .collect(); + let want: Vec<(String, String)> = wai::synthesis::EU_ICONS.iter().map(|(n, d)| (n.to_string(), d.to_string())).collect(); + assert_eq!(pinned, want, "§10.2's table and EU_ICONS differ"); +} -- 2.54.0 (Apple Git-157) From b5094c63293569754744cc74826ee22bc908b3d8 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 05:20:57 -0400 Subject: [PATCH 3/9] synthesis_verify.py: the synthesis corpus's second implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Written from extensions/synthesis.md: strict JSON and JCS of its own, the pinned derivations in its own arithmetic, the digital source type and the C2PA statement written from §5 and §6, rooms through live_rooms_verify.py, Ed25519 and BLAKE3 through the claim-conformance helpers. It agrees with wai-rs on every vector and refuses every mutated copy of the corpus. Co-Authored-By: Claude Opus 5.5 --- wai/live-conformance/synthesis_verify.py | 864 +++++++++++++++++++++++ 1 file changed, 864 insertions(+) create mode 100644 wai/live-conformance/synthesis_verify.py diff --git a/wai/live-conformance/synthesis_verify.py b/wai/live-conformance/synthesis_verify.py new file mode 100644 index 000000000..e37260673 --- /dev/null +++ b/wai/live-conformance/synthesis_verify.py @@ -0,0 +1,864 @@ +#!/usr/bin/env python3 +"""Second implementation of extensions/synthesis.md (`wai.media.synthesis`), over the corpus +`live-conformance/synthesis/`. + +Written from the extension's text. It shares no code with wai-rs: it reads every record as §2 +has it read (strict JSON, the encodings of jwp-receipts §2.4.3), verifies every signature with +WAI's strict Ed25519 rule (claim-conformance/_ed25519.py) and every link hash with BLAKE3 +written from its specification (claim-conformance/_blake3.py), runs the two pinned derivations +of §4 in its own arithmetic, writes the digital source type of §5 and the C2PA statement of §6 +itself, and verifies each room with the live-rooms corpus's second implementation +(live_rooms_verify.py). It reads §10.2's pinned icons and §12's codes from the extension. + + python3 -I synthesis_verify.py + +It gives every vector of the corpus the outcome, the reason, the place and the statements the +corpus states, checks that every reason of §12 refuses a vector, and then mutates copies of the +corpus and requires every copy to fail. Exit status 0 only if all of that holds. Python 3.8 or +later; standard library only. +""" + +import copy +import hashlib +import json +import os +import re +import sys +import unicodedata + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +sys.path.insert(0, os.path.join(HERE, "..", "claim-conformance")) +import live_events_verify as LEV # noqa: E402 +import live_rooms_verify as LRV # noqa: E402 +import _ed25519 as ED # noqa: E402 +from _blake3 import blake3 # noqa: E402 + +EXTENSION = os.path.join(HERE, "..", "extensions", "synthesis.md") +REGISTRY = os.path.join(HERE, "..", "registry", "capabilities.json") + +SIGN_DOMAIN = b"wai:synthesis\x01" # §2 +LINK_DOMAIN = b"wai:synthesis-link\x01" # §2 +OBJECT_DOMAIN = b"moq-jwp:object\x01" # jwp-receipts §2.1 +IPTC = "http://cv.iptc.org/newscodes/digitalsourcetype/" +MODEL_ID = "tag:transaction.science,2026:wai/model/" # §6 +UPSCALE2, DENOISE3 = "wai-synthesis:upscale2", "wai-synthesis:denoise3" +MAX_INT = (1 << 53) - 1 + +CLASSES = ["captured", "reconstructed", "assistive", "synthesized"] # §3.2, in order +STEP_CLASS = {"capture": "captured", "convert": "reconstructed", "assist": "assistive", "generate": "synthesized"} +CAPTURE_SOURCES = ["digitalCapture", "computationalCapture", "screenCapture", "digitalCreation"] +SOURCES = CAPTURE_SOURCES + ["algorithmicallyEnhanced", "trainedAlgorithmicMedia", "compositeWithTrainedAlgorithmicMedia", + "algorithmicMedia", "composite", "compositeCapture", "compositeSynthetic"] # §5 +OVERSIGHT = ["fully_autonomous", "prompt_guided", "human_validated"] +MEDIA = ["audio", "video", "image", "text"] +PATH_TIERS = ["decode-equivalence", "generation-equivalence", "entropy-consistency", "none"] +REVIEW_TIERS = ["decode-equivalence", "generation-equivalence"] + +# Unicode's White_Space property (a term holds none of these, jwp-receipts §2.4.3). +WHITE_SPACE = set("\t\n\x0b\x0c\r \x85\xa0         " + "   

   ") + + +class Refuse(Exception): + def __init__(self, reason, at=None): + super().__init__(reason) + self.reason, self.at = reason, at + + +class Bad(Exception): + """A record that does not read: `record_malformed`.""" + + +# ---------------------------------------------------------------- §2 records + +def strict_json(text): + """RFC 8259, no member named twice, no number but an unsigned integer, no unpaired + surrogate, at most 32 levels.""" + def pairs(ps): + d = {} + for k, v in ps: + if k in d: + raise Bad("a member named twice") + d[k] = v + return d + + def no_float(s): + raise Bad("a number that is not an unsigned integer") + + def integer(s): + if s.startswith("-"): + raise Bad("a negative number") + return int(s) + + try: + v = json.loads(text, object_pairs_hook=pairs, parse_float=no_float, parse_int=integer, parse_constant=no_float) + except (ValueError, RecursionError): + raise Bad("not JSON") + + def walk(x, depth): + if isinstance(x, (dict, list)): + if depth > 32: + raise Bad("too deep") + for y in (x.values() if isinstance(x, dict) else x): + walk(y, depth + 1) + if isinstance(x, dict): + for k in x: + walk(k, depth) + elif isinstance(x, str): + if any(0xD800 <= ord(c) <= 0xDFFF for c in x): + raise Bad("an unpaired surrogate") + elif isinstance(x, bool): + raise Bad("a boolean") + elif isinstance(x, int): + if x > MAX_INT: + raise Bad("an integer past 2^53 - 1") + walk(v, 1) + return v + + +def jstr(s): + out = ['"'] + for c in s: + o = ord(c) + if c == '"': + out.append('\\"') + elif c == "\\": + out.append("\\\\") + elif c == "\b": + out.append("\\b") + elif c == "\f": + out.append("\\f") + elif c == "\n": + out.append("\\n") + elif c == "\r": + out.append("\\r") + elif c == "\t": + out.append("\\t") + elif o < 0x20: + out.append("\\u%04x" % o) + else: + out.append(c) + out.append('"') + return "".join(out) + + +def jcs(v): + """RFC 8785 on the values a record carries.""" + if v is None: + return "null" + if isinstance(v, bool): + raise Bad("a boolean") + if isinstance(v, int): + return str(v) + if isinstance(v, str): + return jstr(v) + if isinstance(v, list): + return "[" + ",".join(jcs(x) for x in v) + "]" + keys = sorted(v, key=lambda k: k.encode("utf-16-be")) + return "{" + ",".join(jstr(k) + ":" + jcs(v[k]) for k in keys) + "}" + + +def link_hash(v): + return blake3(LINK_DOMAIN + jcs(v).encode("utf-8")) + + +def signing_bytes(v): + return SIGN_DOMAIN + jcs({k: x for k, x in v.items() if k != "sig"}).encode("utf-8") + + +def need(cond): + if not cond: + raise Bad("a member breaks its rule") + + +def hexb(v, n): + need(isinstance(v, str) and len(v) == 2 * n and re.fullmatch(r"[0-9a-f]*", v) is not None) + return bytes.fromhex(v) + + +def opt_hex(v, n): + return None if v is None else hexb(v, n) + + +def integer(v): + need(isinstance(v, int) and not isinstance(v, bool) and 0 <= v <= MAX_INT) + return v + + +def term(v): + need(isinstance(v, str) and 0 < len(v.encode("utf-8")) <= 2048 + and not any(c in WHITE_SPACE or unicodedata.category(c) == "Cc" for c in v)) + return v + + +def opt_term(v): + return None if v is None else term(v) + + +def one_of(v, values): + need(isinstance(v, str) and v in values) + return v + + +def members(v, names): + need(isinstance(v, dict) and set(v) == set(names)) + return v + + +def date_time(s): + """RFC 3339 §5.6 date-time, with §5.7's days (jwp-receipts §2.4.3).""" + m = re.fullmatch(r"([0-9]{4})-([0-9]{2})-([0-9]{2})[Tt]([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]+)?([Zz]|[+-]([0-9]{2}):([0-9]{2}))", s or "") + need(m is not None) + y, mo, d, h, mi, sec = (int(m.group(i)) for i in range(1, 7)) + leap = y % 4 == 0 and (y % 100 != 0 or y % 400 == 0) + days = [31, 29 if leap else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + need(1 <= mo <= 12 and 1 <= d <= days[mo - 1] and h <= 23 and mi <= 59 and sec <= 60) + if m.group(9) is not None: + need(int(m.group(9)) <= 23 and int(m.group(10)) <= 59) + + +def read(text, kind, names, fields): + """§2: read one record, then its signature. `fields` reads the kind's own members.""" + try: + v = strict_json(text) + members(v, names) + need(v["kind"] == kind) + signer = hexb(v["signer_pubkey"], 32) + sig = hexb(v["sig"], 64) + if "issued_at" in v: + date_time(v["issued_at"]) + r = fields(v) + except (Bad, KeyError, TypeError, AttributeError): + raise Refuse("record_malformed") + bad = ED.verify(signer, signing_bytes(v), sig) + if bad: + raise Refuse(bad) + r.update(value=v, signer=signer, link=link_hash(v)) + return r + + +# ---------------------------------------------------------------- §3 the origin claim + +ORIGIN = ["kind", "subject", "media", "class", "step", "operation", "record", "source", "models", "oversight", "inputs", + "evidence", "issued_at", "signer_pubkey", "sig"] + + +def subject_of(v): + t = v.get("type") + if t == "object": + members(v, ["type", "content_hash"]) + return ("object", hexb(v["content_hash"], 32)) + if t == "group": + members(v, ["type", "receipt"]) + return ("group", hexb(v["receipt"], 32)) + if t == "track": + members(v, ["type", "track", "from_group"]) + integer(v["from_group"]) + return ("track", hexb(v["track"], 32)) + raise Bad("a subject of no type") + + +def origin_fields(v): + c = {"subject": subject_of(v["subject"]), "media": one_of(v["media"], MEDIA), "class": one_of(v["class"], CLASSES), + "step": one_of(v["step"], list(STEP_CLASS)), "operation": term(v["operation"]), "record": opt_hex(v["record"], 32), + "source": one_of(v["source"], SOURCES), + "oversight": None if v["oversight"] is None else one_of(v["oversight"], OVERSIGHT)} + need(isinstance(v["models"], list) and len(v["models"]) <= 16) + c["models"] = [] + for m in v["models"]: + members(m, ["digest", "type"]) + c["models"].append({"digest": hexb(m["digest"], 32), "trained": one_of(m["type"], ["trained", "algorithmic"]) == "trained"}) + need(len({m["digest"] for m in c["models"]}) == len(c["models"])) + need(isinstance(v["inputs"], list) and len(v["inputs"]) <= 16) + c["inputs"] = [] + for i in v["inputs"]: + members(i, ["content_hash", "origin", "class", "source"]) + c["inputs"].append({"content_hash": hexb(i["content_hash"], 32), "origin": opt_hex(i["origin"], 32), + "class": one_of(i["class"], CLASSES), "source": one_of(i["source"], SOURCES)}) + need(len({i["content_hash"] for i in c["inputs"]}) == len(c["inputs"])) + e = v["evidence"] + if e is None: + c["evidence"] = None + else: + members(e, ["tier", "derivation", "params", "width", "height", "input_samples", "output_samples"]) + need(isinstance(e["params"], dict)) + c["evidence"] = {"tier": one_of(e["tier"], ["replicate", "attest"]), "derivation": term(e["derivation"]), + "params": e["params"], "width": integer(e["width"]), "height": integer(e["height"]), + "input_samples": hexb(e["input_samples"], 32), "output_samples": hexb(e["output_samples"], 32)} + need(c["evidence"]["width"] >= 1 and c["evidence"]["height"] >= 1) + n, m, s = len(c["inputs"]), len(c["models"]), c["step"] + plain = c["oversight"] is None and c["evidence"] is None + if s == "capture": + need(n == 0 and m == 0 and plain and c["source"] in CAPTURE_SOURCES) + elif s == "convert": + need(n >= 1 and m == 0 and plain) + elif s == "assist": + need(n == 1 and c["oversight"] is None and c["evidence"] is not None) + else: + need(m >= 1 and c["oversight"] is not None and c["evidence"] is None) + return c + + +def class_of(c): + """§3.2: the greatest of the step's class and the inputs'.""" + return max([STEP_CLASS[c["step"]]] + [i["class"] for i in c["inputs"]], key=CLASSES.index) + + +def generated_source(c): + """§5.""" + if not any(m["trained"] for m in c["models"]): + return "algorithmicMedia" + if c["oversight"] == "human_validated" or any(i["class"] != "synthesized" for i in c["inputs"]): + return "compositeWithTrainedAlgorithmicMedia" + return "trainedAlgorithmicMedia" + + +def source_of(c): + """§5's table.""" + ins = c["inputs"] + if c["step"] == "capture": + return c["source"] + if c["step"] == "convert": + if len(ins) == 1: + return ins[0]["source"] + if any(i["class"] == "synthesized" for i in ins): + return "compositeSynthetic" + if all(i["source"] in ("digitalCapture", "computationalCapture") for i in ins): + return "compositeCapture" + return "composite" + if c["step"] == "assist": + return ins[0]["source"] if ins[0]["class"] == "synthesized" else "algorithmicallyEnhanced" + return generated_source(c) + + +def statement(c): + """§6, as its JCS text.""" + if len(c["inputs"]) != 1: + actions = [{"action": "c2pa.created", "digitalSourceType": IPTC + c["source"]}] + elif c["step"] == "convert": + actions = [{"action": "c2pa.opened"}, {"action": "c2pa.converted"}] + elif c["step"] == "assist": + actions = [{"action": "c2pa.opened"}, {"action": "c2pa.edited", "digitalSourceType": IPTC + "algorithmicallyEnhanced"}] + else: + actions = [{"action": "c2pa.opened"}, {"action": "c2pa.edited", "digitalSourceType": IPTC + generated_source(c)}] + disclosures = [] + for m in [m for m in c["models"] if m["trained"]]: + data = {"modelType": "c2pa.types.model", "modelIdentifier": MODEL_ID + m["digest"].hex()} + if c["step"] == "generate": + data["contentProfile"] = {"humanOversightLevel": c["oversight"]} + k = len(disclosures) + disclosures.append({"label": "c2pa.ai-disclosure" if k == 0 else "c2pa.ai-disclosure__%d" % k, "data": data}) + return jcs({"actions": actions, "assertions": disclosures}) + + +def payload_of(c): + """§7.2: the class's code, then seven bytes of the claim's link hash.""" + return bytes([CLASSES.index(c["class"])]) + c["link"][:7] + + +# ---------------------------------------------------------------- §4 the derivations + +def upscale2(px, w, h): + out = bytearray(4 * w * h * 3) + ow = 2 * w + + def at(x, y, k): + return px[(y * w + x) * 3 + k] + for y in range(h): + for x in range(w): + x1, y1 = min(x + 1, w - 1), min(y + 1, h - 1) + for k in range(3): + a, b, c, d = at(x, y, k), at(x1, y, k), at(x, y1, k), at(x1, y1, k) + out[((2 * y) * ow + 2 * x) * 3 + k] = a + out[((2 * y) * ow + 2 * x + 1) * 3 + k] = (a + b + 1) >> 1 + out[((2 * y + 1) * ow + 2 * x) * 3 + k] = (a + c + 1) >> 1 + out[((2 * y + 1) * ow + 2 * x + 1) * 3 + k] = (a + b + c + d + 2) >> 2 + return bytes(out) + + +def denoise3(px, w, h): + out = bytearray(w * h * 3) + for y in range(h): + for x in range(w): + for k in range(3): + s = 8 + for j in (-1, 0, 1): + for i in (-1, 0, 1): + weight = (2 if i == 0 else 1) * (2 if j == 0 else 1) + xx, yy = min(max(x + i, 0), w - 1), min(max(y + j, 0), h - 1) + s += weight * px[(yy * w + xx) * 3 + k] + out[(y * w + x) * 3 + k] = s >> 4 + return bytes(out) + + +DERIVATIONS = {UPSCALE2: (upscale2, 4), DENOISE3: (denoise3, 1)} + + +# ---------------------------------------------------------------- §7, §9, §10 the other records + +def marking_fields(v): + return {"subject": hexb(v["subject"], 32), "origin": hexb(v["origin"], 32), "scheme": term(v["scheme"]), + "scheme_digest": hexb(v["scheme_digest"], 32), "payload": hexb(v["payload"], 8)} + + +def detection_fields(v): + return {"subject": hexb(v["subject"], 32), "scheme": term(v["scheme"]), "scheme_digest": hexb(v["scheme_digest"], 32), + "payload": opt_hex(v["payload"], 8)} + + +def review_fields(v): + verdict = one_of(v["verdict"], ["approved", "approved-with-notes", "rejected"]) + notes = opt_hex(v["notes"], 32) + need((notes is None) == (verdict == "approved")) + term(v["decoder"]) + opt_term(v["variant"]) + opt_term(v["role"]) + one_of(v["scope"], ["accuracy", "rendering", "translation"]) + return {"subject": hexb(v["subject"], 32), "origin": hexb(v["origin"], 32), "tier": one_of(v["tier"], REVIEW_TIERS), + "reconstruction": hexb(v["reconstruction"], 32), "verdict": verdict} + + +def policy_fields(v): + need(isinstance(v["classes"], list) and len(v["classes"]) >= 1) + idx = [CLASSES.index(one_of(c, CLASSES)) for c in v["classes"]] + need(all(a < b for a, b in zip(idx, idx[1:]))) + interval = None if v["interval_us"] is None else integer(v["interval_us"]) + need(interval != 0) + return {"classes": list(v["classes"]), "visual": hexb(v["visual"], 32), "audio": opt_hex(v["audio"], 32), + "interval_us": interval, "grace_us": integer(v["grace_us"])} + + +def entry_of(e): + t = e.get("type") + if t == "present": + members(e, ["type", "subject", "origin", "class", "media", "tier", "decoder", "variant", "output"]) + term(e["decoder"]) + opt_term(e["variant"]) + return {"type": t, "subject": hexb(e["subject"], 32), "origin": opt_hex(e["origin"], 32), + "class": None if e["class"] is None else one_of(e["class"], CLASSES), "media": one_of(e["media"], MEDIA), + "tier": one_of(e["tier"], PATH_TIERS), "output": opt_hex(e["output"], 32)} + if t == "shown": + members(e, ["type", "label", "asset", "trigger"]) + return {"type": t, "audio": one_of(e["label"], ["visual", "audio"]) == "audio", "asset": hexb(e["asset"], 32), + "trigger": one_of(e["trigger"], ["first-exposure", "resume", "interval"])} + if t == "resume": + members(e, ["type", "after"]) + one_of(e["after"], ["ad-break", "other"]) + return {"type": t} + if t == "review": + members(e, ["type", "subject", "review"]) + return {"type": t, "subject": hexb(e["subject"], 32), "review": hexb(e["review"], 32)} + raise Bad("an entry of no type") + + +def player_fields(v): + return {"session": hexb(v["session"], 32), "seq": integer(v["seq"]), "prev": opt_hex(v["prev"], 32), "at": integer(v["at"]), + "entry": entry_of(v["entry"])} + + +# ---------------------------------------------------------------- §11 verification + +def generates(env, classes): + """§3.3: an envelope whose capability's registry class is generative-exact or behavioral, or + whose intent is create.""" + if len(env) < 8 or env[:4] != b"WAI1": + return False + n = int.from_bytes(env[4:8], "little") + try: + m = json.loads(env[8:8 + n]) + except ValueError: + return False + cap = m.get("model_requirement", {}).get("capability", "") + return classes.get(cap) in ("generative-exact", "behavioral") or m.get("intent") == "create" + + +def track_digest(room, owner, media, layer): + """live-rooms §6.2.""" + return hashlib.sha256(("-".join([room.hex(), owner, media]) + "\x00" + str(layer)).encode()).digest() + + +def verify(case, classes): + """§11.2: the statements of §11.3, or Refuse with its reason and place.""" + st = {"claims": [], "detections": [], "ai_tracks": [], "reviews_presented": [], "labels": [], "visual_icon": None, "room": None} + + def each(key, kind, names, fields, place): + out = [] + for i, t in enumerate(case[key]): + try: + out.append(read(t, kind, names, fields)) + except Refuse as e: + raise Refuse(e.reason, [place, i]) + return out + + # Step 1. + claims = each("claims", "wai-origin", ORIGIN, origin_fields, "claim") + markings = each("markings", "wai-marking", ["kind", "subject", "origin", "scheme", "scheme_digest", "payload", "issued_at", "signer_pubkey", "sig"], + marking_fields, "marking") + detections = each("detections", "wai-detection", ["kind", "subject", "scheme", "scheme_digest", "payload", "issued_at", "signer_pubkey", "sig"], + detection_fields, "detection") + reviews = each("reviews", "wai-review", ["kind", "subject", "origin", "tier", "reconstruction", "decoder", "variant", "scope", "verdict", "notes", + "role", "issued_at", "signer_pubkey", "sig"], review_fields, "review") + policy = None + if case["label_policy"] is not None: + try: + policy = read(case["label_policy"], "wai-label-policy", ["kind", "classes", "visual", "audio", "interval_us", "grace_us", "issued_at", + "signer_pubkey", "sig"], policy_fields) + except Refuse as e: + raise Refuse(e.reason, ["label_policy"]) + log = each("player", "wai-player", ["kind", "session", "seq", "prev", "at", "entry", "signer_pubkey", "sig"], player_fields, "player") + + by_link = {c["link"]: c for c in claims} + objects = {blake3(OBJECT_DOMAIN + bytes.fromhex(o)): bytes.fromhex(o) for o in case["objects"]} + decoded = {bytes.fromhex(d["object"]): bytes.fromhex(d["samples"]) for d in case["decoded"]} + sha = lambda b: hashlib.sha256(b).digest() # noqa: E731 + + # Step 2. + seen = set() + for i, c in enumerate(claims): + def no(reason): + raise Refuse(reason, ["claim", i]) + key = jcs(c["value"]["subject"]) + if key in seen: + no("claim_conflict") + seen.add(key) + if c["class"] != class_of(c): + no("class_mismatch") + if c["source"] != source_of(c): + no("source_mismatch") + for inp in c["inputs"]: + held = by_link.get(inp["origin"]) + if held is not None and (held["subject"] != ("object", inp["content_hash"]) or held["class"] != inp["class"] + or held["source"] != inp["source"]): + no("input_mismatch") + kind, h = c["subject"] + if kind == "object" and h in objects and generates(objects[h], classes) and c["step"] != "generate": + no("class_understated") + status = None + e = c["evidence"] + if e is not None: + w, hh = e["width"], e["height"] + if e["tier"] == "replicate": + if e["derivation"] not in DERIVATIONS or e["params"] != {} or DERIVATIONS[e["derivation"]][1] * w * hh * 3 > (1 << 26): + no("evidence_invalid") + status = "not-recomputed" + px = decoded.get(c["inputs"][0]["content_hash"]) + if px is not None: + if len(px) != w * hh * 3 or sha(px) != e["input_samples"]: + no("input_samples_mismatch") + if sha(DERIVATIONS[e["derivation"]][0](px, w, hh)) != e["output_samples"]: + no("assistive_recompute_mismatch") + status = "proved" + else: + status = "asserted" + if kind == "object" and h in decoded and sha(decoded[h]) != e["output_samples"]: + no("samples_mismatch") + if case["policy"]["assistive"] == "require-proof" and status != "proved": + no("assistive_not_proved") + st["claims"].append({"link": c["link"].hex(), "class": c["class"], "source": c["source"], "c2pa": statement(c), "assistive": status}) + + # Step 3. + for i, m in enumerate(markings): + c = by_link.get(m["origin"]) + if c is None: + raise Refuse("claim_unknown", ["marking", i]) + if c["subject"] != ("object", m["subject"]) or payload_of(c) != m["payload"]: + raise Refuse("marking_mismatch", ["marking", i]) + for i, d in enumerate(detections): + claim = next((c for c in claims if c["subject"] == ("object", d["subject"])), None) + marking = next((m for m in markings if m["subject"] == d["subject"]), None) + if marking is not None and (marking["scheme"], marking["scheme_digest"]) != (d["scheme"], d["scheme_digest"]): + raise Refuse("detection_mismatch", ["detection", i]) + if d["payload"] is None: + if marking is not None: + raise Refuse("watermark_absent", ["detection", i]) + result = "unmarked" + elif claim is None: + result = "not-checked" + elif d["payload"] != payload_of(claim): + raise Refuse("layers_disagree", ["detection", i]) + else: + result = "agree" + st["detections"].append({"subject": d["subject"].hex(), "result": result}) + if case["policy"]["dual_layer"] == "required": + for i, c in enumerate(claims): + if c["class"] == "synthesized" and c["subject"][0] == "object" and not any(m["origin"] == c["link"] for m in markings): + raise Refuse("layer_missing", ["claim", i]) + + # Step 4. + for i, r in enumerate(reviews): + c = by_link.get(r["origin"]) + if c is None: + raise Refuse("claim_unknown", ["review", i]) + if c["subject"] != ("object", r["subject"]) or c["class"] != "synthesized": + raise Refuse("review_subject_mismatch", ["review", i]) + + # Step 5. + room = case["room"] + if room is not None: + chains = [[bytes.fromhex(e) for e in ch] for ch in room["chains"]] + grants = [bytes.fromhex(g) for g in room["grants"]] + out = LRV.verify_room(bytes.fromhex(room["descriptor"]), grants, room["key_log"], chains) + if out["outcome"] == "refuse": + raise Refuse(out["reason"], ["room"] + list(out["at"])) + st["room"] = out["outcome"] + d = LRV.read_descriptor(bytes.fromhex(room["descriptor"])) + held, _ = LRV.check_grants(d, grants) + by_digest = {g["digest"]: g for g in held} + current = {} + for c_i, r_i, under in out["order"]: + if under is not None: + continue + ev = LEV.read_record(chains[c_i][r_i]) + if not 0x10 <= ev["kind"] <= 0x16: + continue + subject, x = ev["payload"][:32], LRV.read_detail(ev["kind"], ev["payload"][32:]) + if ev["kind"] == 0x10: + current[subject] = x["grant"] + continue + if ev["kind"] == 0x14: + current[subject] = x["new"] + continue + if ev["kind"] == 0x11: + current.pop(subject, None) + continue + if ev["kind"] not in (0x12, 0x15): + continue + g = by_digest.get(current.get(subject)) + model = None + for k, v in (LRV.read_attrs(g["attrs"]) or []) if g else []: + if k == "ai": + model = bytes.fromhex(v) + if model is None: + continue + owner = subject.hex() if not x["slot"] else "_" + x["slot"] + media = {1: "audio", 2: "video", 3: "screen"}[x["media"]] + for layer in range(x["layers"]): + t = track_digest(d["room"], owner, media, layer) + found = next((c for c in claims if c["subject"] == ("track", t) and c["class"] == "synthesized" and c["step"] == "generate" + and any(m["digest"] == model for m in c["models"]) + and (c["signer"] == subject or LRV.is_room_key(d, c["signer"]))), None) + if found is None: + raise Refuse("ai_track_unlabelled", ["room", "record", c_i, r_i]) + st["ai_tracks"].append({"track": t.hex(), "claim": found["link"].hex()}) + + # Step 6. + for j, p in enumerate(log): + ok = p["seq"] == j and p["session"] == log[0]["session"] and p["signer"] == log[0]["signer"] + ok = ok and (p["prev"] is None if j == 0 else (p["prev"] == log[j - 1]["link"] and p["at"] >= log[j - 1]["at"])) + if not ok: + raise Refuse("player_chain_broken", ["player", j]) + for j, p in enumerate(log): + e = p["entry"] + if e["type"] == "present": + c = by_link.get(e["origin"]) if e["origin"] is not None else None + if c is not None and (c["subject"] != ("object", e["subject"]) or e["class"] != c["class"]): + raise Refuse("present_mismatch", ["player", j]) + if e["output"] is not None and e["subject"] in decoded and sha(decoded[e["subject"]]) != e["output"]: + raise Refuse("present_output_irreproducible", ["player", j]) + elif e["type"] == "review": + r = next((r for r in reviews if r["link"] == e["review"]), None) + if r is None: + raise Refuse("claim_unknown", ["player", j]) + last = next((q["entry"] for q in reversed(log[:j]) if q["entry"]["type"] == "present" and q["entry"]["subject"] == e["subject"]), None) + if last is None or r["subject"] != e["subject"]: + raise Refuse("review_subject_mismatch", ["player", j]) + if r["verdict"] == "rejected": + raise Refuse("review_rejected", ["player", j]) + if last["tier"] != r["tier"]: + raise Refuse("review_path_not_exact", ["player", j]) + if last["output"] != r["reconstruction"]: + raise Refuse("review_reconstruction_mismatch", ["player", j]) + st["reviews_presented"].append({"entry": j, "review": e["review"].hex()}) + if policy is not None: + st["visual_icon"] = ICONS.get(policy["visual"].hex()) + for j, p in enumerate(log): + e = p["entry"] + if e["type"] == "shown" and (policy["audio"] if e["audio"] else policy["visual"]) != e["asset"]: + raise Refuse("label_asset_mismatch", ["player", j]) + + def shown_in(audio, lo, hi): + return next((k for k, q in enumerate(log) if q["entry"]["type"] == "shown" and q["entry"]["audio"] == audio and lo <= q["at"] <= hi), None) + + last = {False: None, True: None} + for j, p in enumerate(log): + e = p["entry"] + if e["type"] != "present" or e["class"] not in policy["classes"]: + continue + audio = e["media"] == "audio" + hi = p["at"] + policy["grace_us"] + prev = last[audio] + if prev is None: + trigger, k = "first-exposure", shown_in(audio, 0, hi) + else: + resumes = [q["at"] for q in log[prev + 1:j] if q["entry"]["type"] == "resume"] + if resumes: + trigger, k = "resume", shown_in(audio, resumes[-1], hi) + elif policy["interval_us"] is not None: + trigger, k = "interval", shown_in(audio, max(p["at"] - policy["interval_us"], 0), hi) + else: + last[audio] = j + continue + if k is None: + raise Refuse("label_not_shown", ["player", j]) + st["labels"].append({"entry": j, "trigger": trigger, "shown": k}) + last[audio] = j + return st + + +# ---------------------------------------------------------------- the extension's tables + +def section(text, head): + at = text.index("\n" + head) + nxt = re.search(r"\n#{2,3} ", text[at + 1:]) + return text[at: at + 1 + nxt.start()] if nxt else text[at:] + + +with open(EXTENSION, encoding="utf-8") as _f: + _TEXT = _f.read() +ICONS = {} +for _line in section(_TEXT, "### 10.2 ").splitlines(): + _m = re.fullmatch(r"\| `(LABEL_AI[^`]*)` \| `([0-9a-f]{64})` \|", _line.strip()) + if _m: + ICONS[_m.group(2)] = _m.group(1) +REASONS = [l.split("`")[1] for l in section(_TEXT, "## 12. Reasons").splitlines() if l.startswith("| `")] + + +def outcome(v, classes): + try: + return {"outcome": "accept", "statements": verify(v, classes)} + except Refuse as e: + return {"outcome": "refuse", "reason": e.reason, "at": e.at} + + +def stated(v): + return {k: v[k] for k in ("outcome", "reason", "at", "statements") if k in v} + + +def check(doc, classes): + """Failures of a corpus document: an outcome other than the one stated, or a table that + differs from the extension's.""" + fails = [] + pins = {d["sha256"]: d["file"] for d in doc["eu_icons"]} + if pins != ICONS or len(doc["eu_icons"]) != len(ICONS): + fails.append("the corpus's EU icons differ from §10.2's table") + for v in doc["vectors"]: + try: + got = outcome(v, classes) + except Exception as e: # noqa: BLE001 — a malformed copy fails, whatever it breaks + got = {"outcome": "unreadable: %r" % (e,)} + if got != stated(v): + fails.append("%s: %s" % (v["name"], json.dumps(got)[:300])) + return fails + + +def flip_hex_digit(s, at): + c = "0" if s[at] != "0" else "1" + return s[:at] + c + s[at + 1:] + + +def mutations(doc): + """Copies of the corpus, each with one defect a conforming verifier must catch.""" + vn = {v["name"]: k for k, v in enumerate(doc["vectors"])} + + def mutate(label, f): + d = copy.deepcopy(doc) + f(d["vectors"]) + return label, d + + chain, gen, room, cert = vn["chain"], vn["generated"], vn["ai_room"], vn["certified"] + + def retext(vs, i, key, k, f): + """Rewrite record k of list `key` of vector i as JSON, applying f to its value.""" + v = json.loads(vs[i][key][k]) + f(v) + vs[i][key][k] = json.dumps(v) + + def decoded_byte(vs): + d = vs[chain]["decoded"][0] + d["samples"] = flip_hex_digit(d["samples"], 0) + + def upscale_digit(vs): + retext(vs, chain, "claims", 2, lambda v: v["evidence"].update(output_samples=flip_hex_digit(v["evidence"]["output_samples"], 5))) + + def detection_class(vs): + retext(vs, gen, "detections", 0, lambda v: v.update(payload="00" + v["payload"][2:])) + + return [ + mutate("a digit of the upscale's signature", lambda vs: retext(vs, chain, "claims", 2, lambda v: v.update(sig=flip_hex_digit(v["sig"], 9)))), + mutate("the upscale's output digest changed", upscale_digit), + mutate("a byte of the reconstruction's decoded samples", decoded_byte), + mutate("the reconstruction's claim withheld from the chain", lambda vs: vs[chain]["claims"].pop(1)), + mutate("the generated video's detection reading class captured", detection_class), + mutate("the generated video's marking withheld", lambda vs: vs[gen]["markings"].pop(0)), + mutate("the composite's source stated compositeCapture", + lambda vs: vs[gen]["statements"]["claims"][4].update(source="compositeCapture")), + mutate("the transcode's statement stated as created", + lambda vs: vs[gen]["statements"]["claims"][5].update(c2pa=vs[gen]["statements"]["claims"][1]["c2pa"])), + mutate("the AI host's track claim withheld", lambda vs: vs[room].update(claims=[])), + mutate("the AI host's grant withheld from the room", lambda vs: vs[room]["room"]["grants"].pop(0)), + mutate("the certified log's first label withheld", lambda vs: vs[cert]["player"].pop(0)), + mutate("the label after the break stated as an interval", + lambda vs: vs[cert]["statements"]["labels"][1].update(trigger="interval")), + mutate("the review stated at generation-equivalence", + lambda vs: retext(vs, cert, "reviews", 0, lambda v: v.update(tier="generation-equivalence"))), + mutate("the presented video's decoded frame changed", + lambda vs: vs[cert]["decoded"][0].update(samples=flip_hex_digit(vs[cert]["decoded"][0]["samples"], 3))), + mutate("the policy's visual stated as another pinned icon", + lambda vs: vs[cert]["statements"].update(visual_icon="LABEL_AI_black.svg")), + mutate("the first refusal stated at another place", lambda vs: vs[next(k for k, v in enumerate(vs) if v["outcome"] == "refuse")].update(at=["claim", 1])), + ("an EU icon's pin changed", dict(copy.deepcopy(doc), eu_icons=[dict(doc["eu_icons"][0], sha256="00" * 32)] + doc["eu_icons"][1:])), + ] + + +def main(corpus_dir): + fails = [] + st = ED._self_test() + print(("PASS" if not st else "FAIL") + " Ed25519 strict rule self-test " + ",".join(st)) + if st: + fails.append("ed25519 self-test") + with open(REGISTRY, encoding="utf-8") as f: + classes = {c["capability"]: c["replicate"] for c in json.load(f)["capabilities"]} + with open(os.path.join(corpus_dir, "vectors.json"), encoding="utf-8") as f: + doc = json.load(f) + if len(ICONS) != 24: + fails.append("§10.2's table does not read as 24 pinned icons") + reasons_seen = set() + for v in doc["vectors"]: + try: + got = outcome(v, classes) + except Exception as e: # noqa: BLE001 + got = {"outcome": "unreadable: %r" % (e,)} + ok = got == stated(v) + if v.get("reason"): + reasons_seen.add(v["reason"]) + print(("PASS" if ok else "FAIL") + " vector %s: %s" % (v["name"], got["outcome"] + (" %s at %s" % (got["reason"], got["at"]) if "reason" in got else ""))) + if not ok: + fails.append("%s: %s, the corpus states %s" % (v["name"], json.dumps(got)[:400], json.dumps(stated(v))[:400])) + table = check(doc, classes) + if any("EU icons" in t for t in table): + fails.append("the corpus's EU icons differ from §10.2's table") + missing = [r for r in REASONS if r not in reasons_seen] + if missing: + fails.append("no vector refused with %s" % ", ".join(missing)) + accepted = {v["name"] for v in doc["vectors"] if v["outcome"] == "accept"} + want = {"chain", "assistive_asserted", "generated", "ai_room", "certified"} + if not want <= accepted: + fails.append("vectors not accepted: %s" % ", ".join(sorted(want - accepted))) + print("%s every reason of §12 refuses a vector, and every case of §13 is accepted" % ("PASS" if not missing and want <= accepted else "FAIL")) + for label, d in mutations(doc): + caught = check(d, classes) + print(("PASS" if caught else "FAIL") + " mutated copy fails: %s%s" % (label, " (%s)" % caught[0][:100] if caught else "")) + if not caught: + fails.append("a mutated copy verifies: %s" % label) + for f in fails: + print("FAIL " + f) + print(("PASS" if not fails else "FAIL") + " synthesis corpus: %d vectors" % len(doc["vectors"])) + return 1 if fails else 0 + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("usage: synthesis_verify.py ", file=sys.stderr) + sys.exit(2) + sys.exit(main(sys.argv[1])) -- 2.54.0 (Apple Git-157) From 7beb1cac854f91876ba471e644aac23b08518e9a Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 05:39:51 -0400 Subject: [PATCH 4/9] =?UTF-8?q?wai.media.synthesis:=20registry=20entry=20a?= =?UTF-8?q?nd=20document=20kinds,=20SPEC=20=C2=A75,=20CHANGELOG,=20CI=20st?= =?UTF-8?q?ep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The registry lists the record wai.media.synthesis and its six document kinds; the export is regenerated (registry export --check is up to date). SPEC §5 says what the record is. The conformance workflow runs the module's unit tests, tests/synthesis.rs and synthesis_verify.py, and fires on an edit of the extension, whose tables the tests read. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-conformance.yml | 27 ++++++++++++- wai/CHANGELOG.md | 53 +++++++++++++++++++++++++ wai/REGISTRY.md | 11 +++++- wai/SPEC.md | 27 ++++++++++++- wai/registry/capabilities.json | 56 +++++++++++++++++++++++++++ wai/vocab/v2.ttl | 7 ++++ wai/wai-rs/src/codecs/registry.rs | 10 +++++ 7 files changed, 186 insertions(+), 5 deletions(-) diff --git a/.github/workflows/wai-conformance.yml b/.github/workflows/wai-conformance.yml index de429a1be..3fdf97907 100644 --- a/.github/workflows/wai-conformance.yml +++ b/.github/workflows/wai-conformance.yml @@ -106,6 +106,10 @@ on: # reports, live_ads_verify.py reads that table, and the registry's wire identifiers test # reads its sections. - 'wai/extensions/live-ads.md' + # tests/synthesis.rs requires this extension's §12 to list the codes the reference + # reports and its §10.2 table to pin the reference's icons, synthesis_verify.py reads + # both, and the registry's wire identifiers test reads its sections. + - 'wai/extensions/synthesis.md' - 'wai/wai-web/**' # The audio provenance check below reads the corpora above with this tool. - 'wai/tools/wai_audio_provenance_check.py' @@ -776,7 +780,8 @@ jobs: # live_metering_verify.py (the live-metering corpus), energy_steering_verify.py (the # energy-steering corpus), and live_moderation_verify.py (the live-moderation corpus), and # e2ee_verify.py (the end-to-end encrypted rooms corpus), live_watermark_verify.py (the - # forensic-watermark corpus) and live_ads_verify.py (the live-ads corpus). + # forensic-watermark corpus), live_ads_verify.py (the live-ads corpus) and + # synthesis_verify.py (the synthesis corpus). # Each exits non-zero on any failure or any difference, and the step fails with it. # One platform: what they check is the reading of the text, not the architecture. independent-verifiers: @@ -1091,6 +1096,26 @@ jobs: (cd wai-rs && cargo test --no-default-features --features live_ads --lib wai_video_ssai::) (cd wai-rs && cargo test --no-default-features --features live_ads --test live_ads) python3 -I live-conformance/live_ads_verify.py live-conformance/ads + # wai.media.synthesis (extensions/synthesis.md): the reference module's unit tests (the + # pinned derivations by their rules, the class lattice, a generator's envelope) and + # tests/synthesis.rs (the committed corpus regenerates byte for byte from its fixed seeds + # and no other file is there, wai-rs gives every case its stated outcome and statements, + # §12 lists exactly the reference's codes, each of which the corpus gives, and §10.2's + # table pins exactly the reference's icons); then the corpus's second implementation, + # synthesis_verify.py, written from the extension's text and sharing no code with wai-rs, + # which runs the pinned derivations and writes each digital source type and C2PA + # statement itself, verifies each room with live_rooms_verify.py and every record with + # WAI's strict Ed25519 rule and BLAKE3 from claim-conformance, reads the extension's icon + # and reason tables, and requires every mutated copy of the corpus to fail; standard + # library only, run isolated (-I). + - name: synthesis provenance · wai-rs and synthesis_verify.py + working-directory: wai + shell: bash + run: | + set -euo pipefail + (cd wai-rs && cargo test --no-default-features --features synthesis --lib synthesis::) + (cd wai-rs && cargo test --no-default-features --features synthesis --test synthesis) + python3 -I live-conformance/synthesis_verify.py live-conformance/synthesis # wai.live.e2ee (extensions/confidentiality.md): the corpus's second implementation, # e2ee_verify.py, written from the extension's text and the documents it reads and # sharing no code with the reference (joule-moq's secure_objects and joule-moq-mls, diff --git a/wai/CHANGELOG.md b/wai/CHANGELOG.md index e7c07ec03..5b6e8fc8f 100644 --- a/wai/CHANGELOG.md +++ b/wai/CHANGELOG.md @@ -6,6 +6,59 @@ wire format may change and such changes are recorded here. ## v0.4.0 — current +### `wai.media.synthesis`: what any piece of media is, in two marking layers, with reviews and labels + +- **The extension** ([synthesis](extensions/synthesis.md)) registers the record + `wai.media.synthesis`. An **origin claim** (`wai-origin`) says of one object, group or 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) or synthesized (generated). + It names its step (`capture`, `convert`, `assist`, `generate`), the models the step ran, the + oversight of a generation, and each input by its content hash and the link hash of its own + claim. Its class is the greatest of its step's and its inputs', so it rises along a chain of + derivations and never falls; a transcode of a generated video is synthesized. A codec is a + conversion: it names no model and keeps its input's class and source, as energy-binding §6.1 + has a decode. +- **IPTC and C2PA as functions of the claim.** The digital source type (`digitalCapture`, + `algorithmicallyEnhanced`, `trainedAlgorithmicMedia`, `compositeWithTrainedAlgorithmicMedia`, + `compositeSynthetic` and the rest) follows from the step, the inputs, the models and the + oversight; the C2PA statement is the actions (`c2pa.created` with the source, or + `c2pa.opened` then `c2pa.converted` or `c2pa.edited`) and one `c2pa.ai-disclosure` per trained + model, written in JCS. The mapping keeps timed-text's for captions. +- **Assistive claims checked, not trusted.** Two integer derivations are pinned: a 2× upscale + with `wai.video.int_motion`'s half-sample rounding, and a 3 × 3 binomial denoise. Evidence + names the input's and the output's sample digests; a verifier holding the input's decoded + samples recomputes the output and refuses a mismatch. A float or unpinned step states + `attest` and is reported as asserted; a policy can require proof. +- **Two marking layers.** The signed claim is the first; the second is a watermark whose 8-byte + payload is the claim's class and the head of its link hash. A marking record names the scheme + and the payload embedded, a detection record what a detector read, and a verifier refuses a + detection that disagrees with the claim or finds no mark in a marked object, and, under a + policy that asks for both layers, a synthesized object with no marking. No watermark scheme is + registered: the scheme is a slot a later revision fills. +- **AI participants in rooms.** live-rooms §4.4's grant attribute `ai` names the model that acts + for a participant; its chat, reactions and captions state origin 2 or 3 with that model, and + the room verifier refuses one that does not (`ai_unmarked`, live-rooms §9.2 step 6). Chat and + reaction events of live-events gain an optional `origin` and `model` after their last field. + Every track an AI participant publishes needs a synthesized track claim naming its model. +- **Certified synthesis.** A review (`wai-review`) is a signed verdict over one exact + reconstruction at `decode-equivalence` or `generation-equivalence`. A player presents it only + where its latest presentation of the object holds that tier on its decode path and output the + reconstruction reviewed, so one review holds for every viewer of exact output. +- **Labels.** A label policy (`wai-label-policy`) says which classes are labelled, with which + visual and audible assets, at what interval and grace; a player's signed log (`wai-player`) of + presentations, labels shown, resumptions after a break and reviews presented is held to it: + a label at first exposure, after each interruption and within the interval. The EU icons the + Code of Practice's Annex 1 publishes are pinned by SHA-256; no audible EU label is published. +- **Reference and corpus.** `wai-rs` feature `synthesis` (`src/synthesis.rs`). + `live-conformance/synthesis/`, written from fixed seeds by `wai_synthesis_vectors`: a + capture's chain through a neural codec and both pinned derivations, a float super-resolution + asserted, generated media in both layers, a room with an AI host, a certified and labelled + session, and one refused case per reason. Checked by `tests/synthesis.rs` and by + `live-conformance/synthesis_verify.py`, a second implementation that shares no code with + `wai-rs`, verifies each room with `live_rooms_verify.py`, runs the derivations and writes each + statement itself, and requires every mutated copy of the corpus to fail. The registry lists + the record and its document kinds. + ### wai-quantum 0.6.12: cheaper contraction trees, and the cut-rank table on the GPU 0.6.11 was not published on its own: this release carries it to the package registry, and `wai-quantum-cli` 0.1.0 requires it, since the command line's `metal` feature turns on `quantum_cutrank_metal`. diff --git a/wai/REGISTRY.md b/wai/REGISTRY.md index 5e28e271c..65713c482 100644 --- a/wai/REGISTRY.md +++ b/wai/REGISTRY.md @@ -42,8 +42,8 @@ Every registered string has one kind. `wai.video.manifest` and its siblings; the live records `wai.live.events`, `wai.live.room`, `wai.capture.take`, `wai.live.compose`, `wai.live.recording`, `wai.live.metering`, `wai.live.steering`, `wai.live.moderation`, `wai.live.e2ee`, - `wai.live.watermark` and `wai.live.ads`; timed text, `wai.text.timed`). - It names no payload format. + `wai.live.watermark` and `wai.live.ads`; timed text, `wai.text.timed`; the origin claims of + media, `wai.media.synthesis`). It names no payload format. A derivation or record string is not a payload capability, so it is not dispatchable (SPEC §4): dispatch never takes an envelope's capability that names @@ -206,6 +206,7 @@ and a role are fixed once registered. | `wai.live.room` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content | | `wai.live.steering` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content | | `wai.live.watermark` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content | +| `wai.media.synthesis` | record | registered | — | — | — | — | — | §5 Interactive, temporal & computed content | | `wai.mesh.draco` | payload | registered | volumetric | standalone | royalty-free | standard-unclassified | — | §5 Immersive audio, cinema, volumetric, haptics, HDR | | `wai.mesh.glb` | payload | registered | volumetric | standalone | royalty-free | standard-unclassified | — | §5 Interactive, temporal & computed content | | `wai.meta.dovi` | companion | registered | metadata | companion | proprietary | standard-unclassified | — | §5 Immersive audio, cinema, volumetric, haptics, HDR | @@ -356,11 +357,17 @@ and a role are fixed once registered. | `urn:wai:event:1` | scheme | defined | live-events §8.4 | | `urn:wai:event:1` | catalog | defined | live-events §8.1 | | `wai` | catalog | defined | moq-streaming-format §3 | +| `wai-detection` | document-kind | defined | synthesis §7.3 | | `wai-key-status` | document-kind | defined | jwp-receipts §7.9 | +| `wai-label-policy` | document-kind | defined | synthesis §10.1 | +| `wai-marking` | document-kind | defined | synthesis §7.2 | | `wai-module-grant` | document-kind | defined | jwp-receipts §7.9 | | `wai-module-grants` | document-kind | defined | http-delivery §8.4 | +| `wai-origin` | document-kind | defined | synthesis §3.1 | +| `wai-player` | document-kind | defined | synthesis §10.3 | | `wai-receipt-index` | document-kind | defined | http-delivery §8.2 | | `wai-receipts` | catalog | defined | moq-streaming-format §4.1 | +| `wai-review` | document-kind | defined | synthesis §9.1 | | `wai1` | codecs-prefix | defined | http-delivery §4.1 | | `wai1` | fourcc | provisional | http-delivery Appendix B | | `wai2` | codecs-prefix | defined | http-delivery §4.1 | diff --git a/wai/SPEC.md b/wai/SPEC.md index 5eaeabb59..9a7ceb1d7 100644 --- a/wai/SPEC.md +++ b/wai/SPEC.md @@ -594,8 +594,8 @@ kind: `wai.video.package` and `wai.video.channel`; and the live records `wai.live.events`, `wai.live.room`, `wai.capture.take`, `wai.live.compose`, `wai.live.recording`, `wai.live.metering`, `wai.live.steering`, `wai.live.moderation`, `wai.live.e2ee`, - `wai.live.watermark` and `wai.live.ads`, and timed text, `wai.text.timed`), which names no - payload format. + `wai.live.watermark` and `wai.live.ads`, and timed text, `wai.text.timed`; and the origin + claims of media, `wai.media.synthesis`), which names no payload format. Only payload, companion and refinement capabilities have a media class, a fidelity tier, a replicate class and a role. A derivation or record string is not a payload @@ -1071,6 +1071,29 @@ recomputes the fill and the asset list, refuses a second receipt of one session and holds the spend of every receipt together to the decider grant's ceiling: [`extensions/live-ads.md`](extensions/live-ads.md). +And for the **origin** of any media — `wai.media.synthesis`: 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) +or synthesized (generated). It names each input by the link hash of the input's own claim, and +its class is the greatest of its step's and its inputs', so the class rises along every chain +of derivations and never falls; a generator's own envelope (a `GenerativeDeterministic` or +`Behavioral` capability, or `intent` `create`) cannot be claimed as anything else. The claim's +IPTC digital source type and its C2PA statement (the actions, and one `c2pa.ai-disclosure` +assertion per trained model) are functions of it; a codec's step is a conversion, which asserts +no source of its own, as a decode does under energy-binding §6.1. An assistive claim over one +of two pinned integer derivations (a 2× upscale and a 3 × 3 denoise) is proved by recomputing +the output from the input's decoded samples; any other assistive step can only assert it. The +signed claim is the first of two marking layers; the second is a watermark whose payload is the +claim's class and the head of its link hash, and a detection that disagrees with the claim is +refused (the watermark algorithm is a slot a later revision fills). A room's AI participant, +marked in its grant, states its machine origin in its chat, reactions and captions, and labels +every track it publishes. A review signed over one exact reconstruction is presented only by a +player whose decode path holds the review's tier and whose output is that reconstruction, so one +review holds for every viewer of exact output. A label policy says when a label is due (first +exposure, after each interruption, at an interval), with the EU icons pinned by digest, and a +player's signed log shows whether it was shown: +[`extensions/synthesis.md`](extensions/synthesis.md). + ### Payloads of the WAI-native integer codecs This revision pins a payload format to each WAI-native integer codec whose row named diff --git a/wai/registry/capabilities.json b/wai/registry/capabilities.json index f3a940393..730d005dc 100644 --- a/wai/registry/capabilities.json +++ b/wai/registry/capabilities.json @@ -742,6 +742,20 @@ "edition": null, "registered": "2026-10-20" }, + { + "capability": "wai.media.synthesis", + "kind": "record", + "status": "registered", + "media": null, + "role": null, + "tier": null, + "licence": null, + "replicate": null, + "sets": [], + "spec_section": "§5 Interactive, temporal & computed content", + "edition": null, + "registered": "2026-10-21" + }, { "capability": "wai.mesh.draco", "kind": "payload", @@ -2277,6 +2291,13 @@ "section": "§3", "status": "defined" }, + { + "name": "wai-detection", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§7.3", + "status": "defined" + }, { "name": "wai-key-status", "kind": "document-kind", @@ -2284,6 +2305,20 @@ "section": "§7.9", "status": "defined" }, + { + "name": "wai-label-policy", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§10.1", + "status": "defined" + }, + { + "name": "wai-marking", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§7.2", + "status": "defined" + }, { "name": "wai-module-grant", "kind": "document-kind", @@ -2298,6 +2333,20 @@ "section": "§8.4", "status": "defined" }, + { + "name": "wai-origin", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§3.1", + "status": "defined" + }, + { + "name": "wai-player", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§10.3", + "status": "defined" + }, { "name": "wai-receipt-index", "kind": "document-kind", @@ -2312,6 +2361,13 @@ "section": "§4.1", "status": "defined" }, + { + "name": "wai-review", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§9.1", + "status": "defined" + }, { "name": "wai1", "kind": "codecs-prefix", diff --git a/wai/vocab/v2.ttl b/wai/vocab/v2.ttl index 7dc643ce1..546751b23 100644 --- a/wai/vocab/v2.ttl +++ b/wai/vocab/v2.ttl @@ -604,6 +604,13 @@ wai:wai.live.watermark a wai:Capability ; wai:specSection "§5 Interactive, temporal & computed content" ; wai:registeredOn "2026-10-20"^^xsd:date . +wai:wai.media.synthesis a wai:Capability ; + rdfs:label "wai.media.synthesis" ; + wai:kind wai:kind-record ; + wai:status wai:status-registered ; + wai:specSection "§5 Interactive, temporal & computed content" ; + wai:registeredOn "2026-10-21"^^xsd:date . + wai:wai.mesh.draco a wai:Capability ; rdfs:label "wai.mesh.draco" ; wai:kind wai:kind-payload ; diff --git a/wai/wai-rs/src/codecs/registry.rs b/wai/wai-rs/src/codecs/registry.rs index 4cd0e0c41..0576c6197 100644 --- a/wai/wai-rs/src/codecs/registry.rs +++ b/wai/wai-rs/src/codecs/registry.rs @@ -221,6 +221,7 @@ pub const CAP_LIVE_MODERATION: &str = "wai.live.moderation"; pub const CAP_LIVE_E2EE: &str = "wai.live.e2ee"; pub const CAP_LIVE_WATERMARK: &str = "wai.live.watermark"; pub const CAP_LIVE_ADS: &str = "wai.live.ads"; +pub const CAP_MEDIA_SYNTHESIS: &str = "wai.media.synthesis"; /// One row per string SPEC §5 registers that is a payload, companion or record: /// `(string, SPEC section, edition, registered)`. @@ -310,6 +311,7 @@ const ROWS: &[(&str, &str, Option<&str>, &str)] = &[ (CAP_LIVE_E2EE, S_INTERACTIVE, None, "2026-10-19"), (CAP_LIVE_WATERMARK, S_INTERACTIVE, None, "2026-10-20"), (CAP_LIVE_ADS, S_INTERACTIVE, None, "2026-10-20"), + (CAP_MEDIA_SYNTHESIS, S_INTERACTIVE, None, "2026-10-21"), (CAP_AUDIO_IAMF, S_IMMERSIVE, None, "2026-06-17"), (CAP_AUDIO_AC4, S_IMMERSIVE, None, "2026-06-17"), (CAP_AUDIO_IVAS, S_IMMERSIVE, None, "2026-06-17"), @@ -362,6 +364,7 @@ const RECORDS: &[&str] = &[ CAP_LIVE_METERING, CAP_LIVE_STEERING, CAP_TEXT_TIMED, CAP_LIVE_MODERATION, CAP_LIVE_E2EE, CAP_LIVE_WATERMARK, CAP_LIVE_ADS, + CAP_MEDIA_SYNTHESIS, ]; /// A named identifier SPEC uses that has the shape of a capability string but is @@ -518,6 +521,7 @@ const LIVE_MODERATION: &str = "extensions/live-moderation.md"; const CONFIDENTIALITY: &str = "extensions/confidentiality.md"; const FORENSIC_WATERMARK: &str = "extensions/forensic-watermark.md"; const LIVE_ADS: &str = "extensions/live-ads.md"; +const SYNTHESIS: &str = "extensions/synthesis.md"; const fn wire(name: &'static str, kind: WireKind, document: &'static str, section: &'static str, status: WireStatus) -> WireIdentifier { @@ -615,6 +619,12 @@ pub const WIRE_IDENTIFIERS: &[WireIdentifier] = &[ wire("WWD1", WireKind::Magic, FORENSIC_WATERMARK, "§8.1", WireStatus::Defined), wire("WAP1", WireKind::Magic, LIVE_ADS, "§3.1", WireStatus::Defined), wire("WAB1", WireKind::Magic, LIVE_ADS, "§4.2", WireStatus::Defined), + wire("wai-origin", WireKind::DocumentKind, SYNTHESIS, "§3.1", WireStatus::Defined), + wire("wai-marking", WireKind::DocumentKind, SYNTHESIS, "§7.2", WireStatus::Defined), + wire("wai-detection", WireKind::DocumentKind, SYNTHESIS, "§7.3", WireStatus::Defined), + wire("wai-review", WireKind::DocumentKind, SYNTHESIS, "§9.1", WireStatus::Defined), + wire("wai-label-policy", WireKind::DocumentKind, SYNTHESIS, "§10.1", WireStatus::Defined), + wire("wai-player", WireKind::DocumentKind, SYNTHESIS, "§10.3", WireStatus::Defined), ]; /// A string with the shape of a capability that the reference implementation uses -- 2.54.0 (Apple Git-157) From 856f7085de5e7d7906ac4f3ace83dbf2a1e80108 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 05:39:51 -0400 Subject: [PATCH 5/9] synthesis, live rooms: let-chains where clippy asks; wai-live-web reads a chat's origin wai-live-web's decoded view of a chat carries its stated origin and model, and its chat payload states none, as before. Co-Authored-By: Claude Opus 5.5 --- wai/wai-live-web/src/lib.rs | 5 +- wai/wai-rs/src/live_room.rs | 8 +-- wai/wai-rs/src/synthesis.rs | 83 +++++++++++++++--------------- wai/wai-rs/src/synthesis/corpus.rs | 5 +- 4 files changed, 53 insertions(+), 48 deletions(-) diff --git a/wai/wai-live-web/src/lib.rs b/wai/wai-live-web/src/lib.rs index 597da7271..7c18b3bff 100644 --- a/wai/wai-live-web/src/lib.rs +++ b/wai/wai-live-web/src/lib.rs @@ -451,7 +451,8 @@ fn payload_json(ev: &Event) -> Value { "lang": c.lang, "text": c.text, }), - Payload::Chat { text } => json!({"text": text}), + Payload::Chat { text, origin: None } => json!({"text": text}), + Payload::Chat { text, origin: Some(o) } => json!({"text": text, "origin": o.origin, "model": o.model.map(|m| hex(&m))}), Payload::Marker(m) => json!({"marker": format!("{m:?}")}), Payload::Reaction(r) => json!({"reaction": format!("{r:?}")}), Payload::Lifecycle { kind: k, subject, detail } => { @@ -535,7 +536,7 @@ pub fn caption_payload(duration_ms: u32, origin: u8, model: Option, lang #[wasm_bindgen(js_name = chatPayload)] pub fn chat_payload(text: String) -> Vec { - Payload::Chat { text }.encode() + Payload::Chat { text, origin: None }.encode() } /// A lifecycle payload (live-rooms §7.2) from its JSON description: `kind` (`join`, diff --git a/wai/wai-rs/src/live_room.rs b/wai/wai-rs/src/live_room.rs index 3d763da11..f612617df 100644 --- a/wai/wai-rs/src/live_room.rs +++ b/wai/wai-rs/src/live_room.rs @@ -2031,10 +2031,10 @@ fn step(replay: &mut Replay, verifier: &mut RoomVerifier, seen: &mut Seen, b: &[ } } // f. An AI participant's chat, reaction and caption state their machine origin (§4.4). - if let Some(a) = verifier.accepted().last() { - if !ai_marked(&h.grant, &a.payload) { - return Err(Reason::AiUnmarked); - } + if let Some(a) = verifier.accepted().last() + && !ai_marked(&h.grant, &a.payload) + { + return Err(Reason::AiUnmarked); } Ok(Some(h.digest)) } diff --git a/wai/wai-rs/src/synthesis.rs b/wai/wai-rs/src/synthesis.rs index 97fe1938b..d58099cab 100644 --- a/wai/wai-rs/src/synthesis.rs +++ b/wai/wai-rs/src/synthesis.rs @@ -373,10 +373,10 @@ fn read_record(text: &str, kind: &str, members: &[&str]) -> Result<(Value, [u8; if hexn::<64>(&o["sig"]).is_none() { return Err(Reason::RecordMalformed); } - if let Some(at) = o.get("issued_at") { - if !at.as_str().is_some_and(rfc3339) { - return Err(Reason::RecordMalformed); - } + if let Some(at) = o.get("issued_at") + && !at.as_str().is_some_and(rfc3339) + { + return Err(Reason::RecordMalformed); } let link = link_hash(&v); Ok((v, signer, link)) @@ -1113,16 +1113,17 @@ fn run(case: &Case) -> Step6 { return Err(e(Reason::SourceMismatch)); } for input in &c.inputs { - if let Some(held) = input.origin.and_then(|h| by_link.get(&h)) { - if held.subject != Subject::Object(input.content_hash) || held.class != input.class || held.source != input.source { - return Err(e(Reason::InputMismatch)); - } + if let Some(held) = input.origin.and_then(|h| by_link.get(&h)) + && (held.subject != Subject::Object(input.content_hash) || held.class != input.class || held.source != input.source) + { + return Err(e(Reason::InputMismatch)); } } - if let Subject::Object(h) = c.subject { - if objects.get(&h).is_some_and(|env| generates(env)) && c.step != Step::Generate { - return Err(e(Reason::ClassUnderstated)); - } + if let Subject::Object(h) = c.subject + && objects.get(&h).is_some_and(|env| generates(env)) + && c.step != Step::Generate + { + return Err(e(Reason::ClassUnderstated)); } let mut status = Value::Null; if let Some(ev) = &c.evidence { @@ -1130,23 +1131,23 @@ fn run(case: &Case) -> Step6 { return Err(e(Reason::EvidenceInvalid)); } status = json!(if ev.replicate { "not-recomputed" } else { "asserted" }); - if ev.replicate { - if let Some(input) = decoded.get(&c.inputs[0].content_hash) { - let (w, h) = (ev.width as usize, ev.height as usize); - if input.len() != w * h * 3 || sha256(&[input]) != ev.input_samples { - return Err(e(Reason::InputSamplesMismatch)); - } - let out = derive(&ev.derivation, input, w, h).expect("a pinned derivation"); - if sha256(&[&out]) != ev.output_samples { - return Err(e(Reason::AssistiveRecomputeMismatch)); - } - status = json!("proved"); + if ev.replicate + && let Some(input) = decoded.get(&c.inputs[0].content_hash) + { + let (w, h) = (ev.width as usize, ev.height as usize); + if input.len() != w * h * 3 || sha256(&[input]) != ev.input_samples { + return Err(e(Reason::InputSamplesMismatch)); } + let out = derive(&ev.derivation, input, w, h).expect("a pinned derivation"); + if sha256(&[&out]) != ev.output_samples { + return Err(e(Reason::AssistiveRecomputeMismatch)); + } + status = json!("proved"); } - if let Subject::Object(h) = c.subject { - if decoded.get(&h).is_some_and(|b| sha256(&[b]) != ev.output_samples) { - return Err(e(Reason::SamplesMismatch)); - } + if let Subject::Object(h) = c.subject + && decoded.get(&h).is_some_and(|b| sha256(&[b]) != ev.output_samples) + { + return Err(e(Reason::SamplesMismatch)); } if case.policy.require_proof && status != "proved" { return Err(e(Reason::AssistiveNotProved)); @@ -1172,10 +1173,10 @@ fn run(case: &Case) -> Step6 { let e = |r| (At::Detection(i), r); let claim = claims.iter().find(|c| c.subject == Subject::Object(d.subject)); let marking = markings.iter().find(|m| m.subject == d.subject); - if let Some(m) = marking { - if m.scheme != d.scheme || m.scheme_digest != d.scheme_digest { - return Err(e(Reason::DetectionMismatch)); - } + if let Some(m) = marking + && (m.scheme != d.scheme || m.scheme_digest != d.scheme_digest) + { + return Err(e(Reason::DetectionMismatch)); } let result = match (d.payload, claim) { (None, _) if marking.is_some() => return Err(e(Reason::WatermarkAbsent)), @@ -1272,15 +1273,15 @@ fn run(case: &Case) -> Step6 { let e = |r| (At::Player(j), r); match &p.entry { Entry::Present { subject, origin, class, output, .. } => { - if let Some(c) = origin.and_then(|h| by_link.get(&h)) { - if c.subject != Subject::Object(*subject) || *class != Some(c.class) { - return Err(e(Reason::PresentMismatch)); - } + if let Some(c) = origin.and_then(|h| by_link.get(&h)) + && (c.subject != Subject::Object(*subject) || *class != Some(c.class)) + { + return Err(e(Reason::PresentMismatch)); } - if let (Some(o), Some(b)) = (output, decoded.get(subject)) { - if sha256(&[b]) != *o { - return Err(e(Reason::PresentOutputIrreproducible)); - } + if let (Some(o), Some(b)) = (output, decoded.get(subject)) + && sha256(&[b]) != *o + { + return Err(e(Reason::PresentOutputIrreproducible)); } } Entry::Review { subject, review } => { @@ -1375,8 +1376,8 @@ mod tests { let mut dot = vec![0u8; 3 * 3 * 3]; dot[(3 + 1) * 3] = 160; let d = denoise3(&dot, 3, 3); - assert_eq!(d[(3 + 1) * 3], (160 * 4 + 8) >> 4); - assert_eq!(d[0], (160 + 8) >> 4); + assert_eq!(d[(3 + 1) * 3] as u32, (160 * 4 + 8) >> 4); + assert_eq!(d[0] as u32, (160 + 8) >> 4); } #[test] diff --git a/wai/wai-rs/src/synthesis/corpus.rs b/wai/wai-rs/src/synthesis/corpus.rs index a84eb8d71..382580e07 100644 --- a/wai/wai-rs/src/synthesis/corpus.rs +++ b/wai/wai-rs/src/synthesis/corpus.rs @@ -400,6 +400,9 @@ fn resume() -> Value { const MODEL: &str = "voice agent model"; +/// One principal's chain being written: its index, its records, its next seq and its head. +type Chain = (u8, Vec>, u64, [u8; 32]); + /// A room with an AI host whose grant names its model (live-rooms §4.4), a viewer, and the /// host publishing speech. `said`: the origin its chat states. fn ai_room(said: Option) -> RoomInput { @@ -450,7 +453,7 @@ fn ai_room(said: Option) -> RoomInput { }; let host = grant(AI_HOST, 0, vec![0], vec![("ai".into(), hex(&label(MODEL)))]); let viewer = grant(VIEWER, 1, vec![], vec![]); - let mut chains: Vec<(u8, Vec>, u64, [u8; 32])> = vec![]; + let mut chains: Vec = vec![]; let mut emit = |who: u8, t: u64, p: Payload| { let i = match chains.iter().position(|c| c.0 == who) { Some(i) => i, -- 2.54.0 (Apple Git-157) From 720c40e9ad03a06ff97c820f314bdf14f16b1f88 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 05:42:12 -0400 Subject: [PATCH 6/9] synthesis corpus: a reconstruction whose samples make both derivations' roundings decide The reconstruction cleared the photo's low three bits, so every pair of samples summed to an even number and the upscale's rounding never decided a sample: a falsification that dropped it went uncaught. It now quantises to multiples of 3. Co-Authored-By: Claude Opus 5.5 --- wai/live-conformance/synthesis/vectors.json | 42 ++++++++++----------- wai/wai-rs/src/synthesis/corpus.rs | 5 ++- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/wai/live-conformance/synthesis/vectors.json b/wai/live-conformance/synthesis/vectors.json index 5dd70b7b8..838c58ee3 100644 --- a/wai/live-conformance/synthesis/vectors.json +++ b/wai/live-conformance/synthesis/vectors.json @@ -109,8 +109,8 @@ "claims": [ "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", - "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"5b7f404099ea0a38ea23e2c5afd94204c52c6c1c538ef65f5f77492a276b235b\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"33fad4cc0ce6211b8998d6b5da2b6b1f95f309263c20204bc5a2666e05c777ca9983b2292eef37dd8be07c607f75f7ed2124f73e549ef410085ff56d3140fc01\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}", - "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:denoise3\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"d4cd332c1efaf29d4be4430b587cc22e4dd07f9a92282a15fd33c4a7fba078bd\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:denoise3\",\"oversight\":null,\"record\":null,\"sig\":\"cead22a7570c027f963e4db55d492ba2988e57f5976504b62f77d2d1637f8b35ec044a3edaee2300195172e949a04029f239de44a7ddf66bcbac46c4b7fd8e03\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"bbeddbfbea7af91c2a151e23d18e24223bc5402d73438922f56d00d9cc9f3924\",\"type\":\"object\"}}" + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"6ee5006f705630e934216406a83b76d1115f02ce90970b633f68abede9a5d484\",\"output_samples\":\"9cf4a56cc146a782559d36953010f79ddfbc621a0178460d46ca00693652a2e4\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"a0d1198904652fedd4325ec7bf8096bc2684a86e4cec560354c499d5b5628b42ef9a7641f4924ae3ea883ca700dca0f44a2a863024b134574346535899814204\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}", + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:denoise3\",\"height\":3,\"input_samples\":\"6ee5006f705630e934216406a83b76d1115f02ce90970b633f68abede9a5d484\",\"output_samples\":\"69c0ec2d065f3818b39074d0046718e384e00616f69b33fcd41251b62e5d65cf\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:denoise3\",\"oversight\":null,\"record\":null,\"sig\":\"1a8c49f4478f946bfed801951643f70d7fbad455e7e0c11b18e14dda3c5c0d4b592c9e31525d59dc8ddff6a1c9b13f14cad27c57513ef5a48619a87b9c917f0b\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"bbeddbfbea7af91c2a151e23d18e24223bc5402d73438922f56d00d9cc9f3924\",\"type\":\"object\"}}" ], "markings": [], "detections": [], @@ -126,15 +126,15 @@ "decoded": [ { "object": "5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2", - "samples": "8800f880a088b828c028586818104058b800c0b040e858f8f860e0e0e0f8d0e870f8b068" + "samples": "8d03fc87a587bd2dc32a5d691b153f5dba03c6b445e757f9f960e1e1e7fccfed72f9b16c" }, { "object": "6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0", - "samples": "8800f88450c080a0889c64a4b828c070409428586828586850089c5e5a706cac44948c62bc6c80a262988858b08858b018104038642058b8008cb420c0b040d4849ce858f8e858f88838909282869ccc7cb2cc6ac8cc58dca884f084b0f084b0f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068" + "samples": "8d03fc8a54c287a587a269a5bd2dc37445962a5d692a5d69540c9e635e7172b0459a9065c27184a5659b895ab1895ab11b153f3c68215dba0392b724c6b445d7869fe757f9e757f98a3b909586889fd180b5d16ecbd15cddaa87f084b3f084b3f960e1eda4efe1e7fcd8eab7cfed72e4cf6ff9b16cf9b16cf960e1eda4efe1e7fcd8eab7cfed72e4cf6ff9b16cf9b16c" }, { "object": "bbeddbfbea7af91c2a151e23d18e24223bc5402d73438922f56d00d9cc9f3924", - "samples": "6f2db185678e91658d7155917247898e8d71b1997abe7a9fc06fb9c2b5a4d3c984eaaa82" + "samples": "7430b38a6b8f95698f735893754a8a929173b49c7dbf7ba1c171bac4b9a7d4cd87ebac85" } ], "room": null, @@ -156,14 +156,14 @@ "assistive": null }, { - "link": "f1399393f092c48ba1058b0ffb43694fd6dfda3f943bffd4a1f2860e127a72e7", + "link": "3fc547996fadb46eedc47c44c00c8a232888a27e1aa1dbc2f41a1073fb2051e0", "class": "assistive", "source": "algorithmicallyEnhanced", "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.edited\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicallyEnhanced\"}],\"assertions\":[]}", "assistive": "proved" }, { - "link": "729b1bcdad65da15e543b465321304fb698a6a82d5da09e75bbd5eab2a4dbe89", + "link": "e0672490caf6fec9d6e974ba2878d3873b287a80ec6f3c1b0fb3e1135ebcb3f5", "class": "assistive", "source": "algorithmicallyEnhanced", "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.edited\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicallyEnhanced\"}],\"assertions\":[]}", @@ -188,7 +188,7 @@ "claims": [ "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", - "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"super-resolution\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"e9aebdaebbd8ad1ef951b3d2d0cc62272cfcd73f1b69d11363d361826f458b2d\",\"params\":{\"scale\":2},\"tier\":\"attest\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[{\"digest\":\"e2ea8c07e5e2e2c846c97de6f5f79ab21593563603420e70736ab7f22ca451d4\",\"type\":\"trained\"}],\"operation\":\"super-resolution\",\"oversight\":null,\"record\":null,\"sig\":\"0cf70135dcca61696a1cf36b7dfff01475d0730495fad8c68e11a7b5d7eb62fff2be5345897dca3cb5c9b1ff0ca1ef7dde333a5d1dc0cabe7d34c246ce4b1f08\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"d4b33e2b9bad56645fcf0b7856b9009fecd2b4e5e587eeb791a0da87daa601ce\",\"type\":\"object\"}}" + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"super-resolution\",\"height\":3,\"input_samples\":\"6ee5006f705630e934216406a83b76d1115f02ce90970b633f68abede9a5d484\",\"output_samples\":\"e9aebdaebbd8ad1ef951b3d2d0cc62272cfcd73f1b69d11363d361826f458b2d\",\"params\":{\"scale\":2},\"tier\":\"attest\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[{\"digest\":\"e2ea8c07e5e2e2c846c97de6f5f79ab21593563603420e70736ab7f22ca451d4\",\"type\":\"trained\"}],\"operation\":\"super-resolution\",\"oversight\":null,\"record\":null,\"sig\":\"8f3ea73024ea875df6ec23ea0f79b33e760ebb2036b356ad6d2f8813a620b07ddfc27d8e6d2ece330a82db3d5df571c80b3b32880a9bb8e480d12bce3d3e130b\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"d4b33e2b9bad56645fcf0b7856b9009fecd2b4e5e587eeb791a0da87daa601ce\",\"type\":\"object\"}}" ], "markings": [], "detections": [], @@ -216,7 +216,7 @@ "assistive": null }, { - "link": "2d4695995c98f882d8f9371be30ba3d9fe745ef04e53adfda31c51e2070a6b43", + "link": "efe4ca228b9ffa4bc4e57ec985a723f1b66804cefe3d8badfe4085d26757d950", "class": "assistive", "source": "algorithmicallyEnhanced", "c2pa": "{\"actions\":[{\"action\":\"c2pa.opened\"},{\"action\":\"c2pa.edited\",\"digitalSourceType\":\"http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicallyEnhanced\"}],\"assertions\":[{\"data\":{\"modelIdentifier\":\"tag:transaction.science,2026:wai/model/e2ea8c07e5e2e2c846c97de6f5f79ab21593563603420e70736ab7f22ca451d4\",\"modelType\":\"c2pa.types.model\"},\"label\":\"c2pa.ai-disclosure\"}]}", @@ -712,7 +712,7 @@ "claims": [ "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", - "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale3\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"5b7f404099ea0a38ea23e2c5afd94204c52c6c1c538ef65f5f77492a276b235b\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"8b4a1b88633cef43d4fc4de5be5d1b89c8454cf31c1841687d4ed4c0860ac38f92f54c8e4d2e18fd73eea3b5b2d94c7b50d852103de49a133a46d8b6585bed02\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale3\",\"height\":3,\"input_samples\":\"6ee5006f705630e934216406a83b76d1115f02ce90970b633f68abede9a5d484\",\"output_samples\":\"9cf4a56cc146a782559d36953010f79ddfbc621a0178460d46ca00693652a2e4\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"a33cf2459e891d488eb1f70c6e09629535b6267bb119636abfb6bee6a2cb0158c4634e9c6ede2a2492e6fc1deb4c0fd1e0cca48593c9062ab7905e96a4b43208\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" ], "markings": [], "detections": [], @@ -739,7 +739,7 @@ "claims": [ "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", - "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"83a041d31be9a9e43824a8cad0d33ff64fc41d0c359ce9414bf2abda7d7b4698\",\"output_samples\":\"5b7f404099ea0a38ea23e2c5afd94204c52c6c1c538ef65f5f77492a276b235b\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"702489474ef131b989adb9b612af74f0942c8040cb5dde81d6f34fdc03352fac87f21bae59b3d83e2958c2f409f4e366055bcca1abf3fbe38799ea14c5e72002\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"83a041d31be9a9e43824a8cad0d33ff64fc41d0c359ce9414bf2abda7d7b4698\",\"output_samples\":\"9cf4a56cc146a782559d36953010f79ddfbc621a0178460d46ca00693652a2e4\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"f8f775eef9e262f0c1843099c157fd153142fc5e2a6bd6d866ef08687d631af9064bb8f1ab71f5f70879b5c56bcedccf0d948177f868e487ced048b74e302804\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" ], "markings": [], "detections": [], @@ -750,15 +750,15 @@ "decoded": [ { "object": "5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2", - "samples": "8800f880a088b828c028586818104058b800c0b040e858f8f860e0e0e0f8d0e870f8b068" + "samples": "8d03fc87a587bd2dc32a5d691b153f5dba03c6b445e757f9f960e1e1e7fccfed72f9b16c" }, { "object": "6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0", - "samples": "8800f88450c080a0889c64a4b828c070409428586828586850089c5e5a706cac44948c62bc6c80a262988858b08858b018104038642058b8008cb420c0b040d4849ce858f8e858f88838909282869ccc7cb2cc6ac8cc58dca884f084b0f084b0f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068" + "samples": "8d03fc8a54c287a587a269a5bd2dc37445962a5d692a5d69540c9e635e7172b0459a9065c27184a5659b895ab1895ab11b153f3c68215dba0392b724c6b445d7869fe757f9e757f98a3b909586889fd180b5d16ecbd15cddaa87f084b3f084b3f960e1eda4efe1e7fcd8eab7cfed72e4cf6ff9b16cf9b16cf960e1eda4efe1e7fcd8eab7cfed72e4cf6ff9b16cf9b16c" }, { "object": "bbeddbfbea7af91c2a151e23d18e24223bc5402d73438922f56d00d9cc9f3924", - "samples": "6f2db185678e91658d7155917247898e8d71b1997abe7a9fc06fb9c2b5a4d3c984eaaa82" + "samples": "7430b38a6b8f95698f735893754a8a929173b49c7dbf7ba1c171bac4b9a7d4cd87ebac85" } ], "room": null, @@ -779,7 +779,7 @@ "claims": [ "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", - "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"ca2b468d7dfbe78a0184b49537c2955e5670239e18a2f36d0c1f44f313d59bdb\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"bc02fb2fe9564590405af2d8a400371cd4578dc53794f86fa4fe6824e77458b3135ed776beecc4ab445c5d6c15934aebf97f3f77a6c7263069ff4dfdeb2d840b\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"6ee5006f705630e934216406a83b76d1115f02ce90970b633f68abede9a5d484\",\"output_samples\":\"abe1854e7bf732fd112b2e422e0b394cb56905aa9da119143c8f1210e35c4355\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"bb41dee7d220579d2aac13a53689577f1f92cededc77753af2f53fc5c39f7b2d6df9f4a8ba4edd9c1bce91a37a626eb39ccde18d193eb1ace313febdc528b702\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" ], "markings": [], "detections": [], @@ -790,11 +790,11 @@ "decoded": [ { "object": "5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2", - "samples": "8800f880a088b828c028586818104058b800c0b040e858f8f860e0e0e0f8d0e870f8b068" + "samples": "8d03fc87a587bd2dc32a5d691b153f5dba03c6b445e757f9f960e1e1e7fccfed72f9b16c" }, { "object": "6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0", - "samples": "8800f88450c080e0889c64a4b828c070409428586828586850089c5e5a706cac44948c62bc6c80a262988858b08858b018104038642058b8008cb420c0b040d4849ce858f8e858f88838909282869ccc7cb2cc6ac8cc58dca884f084b0f084b0f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068" + "samples": "8d03fc8a54c287e587a269a5bd2dc37445962a5d692a5d69540c9e635e7172b0459a9065c27184a5659b895ab1895ab11b153f3c68215dba0392b724c6b445d7869fe757f9e757f98a3b909586889fd180b5d16ecbd15cddaa87f084b3f084b3f960e1eda4efe1e7fcd8eab7cfed72e4cf6ff9b16cf9b16cf960e1eda4efe1e7fcd8eab7cfed72e4cf6ff9b16cf9b16c" } ], "room": null, @@ -815,7 +815,7 @@ "claims": [ "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", - "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"5b7f404099ea0a38ea23e2c5afd94204c52c6c1c538ef65f5f77492a276b235b\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"33fad4cc0ce6211b8998d6b5da2b6b1f95f309263c20204bc5a2666e05c777ca9983b2292eef37dd8be07c607f75f7ed2124f73e549ef410085ff56d3140fc01\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"wai-synthesis:upscale2\",\"height\":3,\"input_samples\":\"6ee5006f705630e934216406a83b76d1115f02ce90970b633f68abede9a5d484\",\"output_samples\":\"9cf4a56cc146a782559d36953010f79ddfbc621a0178460d46ca00693652a2e4\",\"params\":{},\"tier\":\"replicate\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai-synthesis:upscale2\",\"oversight\":null,\"record\":null,\"sig\":\"a0d1198904652fedd4325ec7bf8096bc2684a86e4cec560354c499d5b5628b42ef9a7641f4924ae3ea883ca700dca0f44a2a863024b134574346535899814204\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0\",\"type\":\"object\"}}" ], "markings": [], "detections": [], @@ -826,11 +826,11 @@ "decoded": [ { "object": "5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2", - "samples": "8800f880a088b828c028586818104058b800c0b040e858f8f860e0e0e0f8d0e870f8b068" + "samples": "8d03fc87a587bd2dc32a5d691b153f5dba03c6b445e757f9f960e1e1e7fccfed72f9b16c" }, { "object": "6579d92c519641fbaa6ac9d42e05c3b87f64c4237b32e268cb54d8588773f6e0", - "samples": "8800f88450c080e0889c64a4b828c070409428586828586850089c5e5a706cac44948c62bc6c80a262988858b08858b018104038642058b8008cb420c0b040d4849ce858f8e858f88838909282869ccc7cb2cc6ac8cc58dca884f084b0f084b0f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068f860e0eca0ece0e0f8d8e4b4d0e870e4cc6cf8b068f8b068" + "samples": "8d03fc8a54c287e587a269a5bd2dc37445962a5d692a5d69540c9e635e7172b0459a9065c27184a5659b895ab1895ab11b153f3c68215dba0392b724c6b445d7869fe757f9e757f98a3b909586889fd180b5d16ecbd15cddaa87f084b3f084b3f960e1eda4efe1e7fcd8eab7cfed72e4cf6ff9b16cf9b16cf960e1eda4efe1e7fcd8eab7cfed72e4cf6ff9b16cf9b16c" } ], "room": null, @@ -851,7 +851,7 @@ "claims": [ "{\"class\":\"captured\",\"evidence\":null,\"inputs\":[],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"capture\",\"oversight\":null,\"record\":null,\"sig\":\"80a29a68d5c2e94c740854784f5b7888baf3455414fb7972165fa420d2270c41cc98f1b6fdef1d9d742d335da9d9cc667d9389c938e47af7a7ea62cb1e4e9203\",\"signer_pubkey\":\"32a9d511e269b29d32c9a1e075f2711ab1ac857448fd64631c852b2abf5a3199\",\"source\":\"digitalCapture\",\"step\":\"capture\",\"subject\":{\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"type\":\"object\"}}", "{\"class\":\"reconstructed\",\"evidence\":null,\"inputs\":[{\"class\":\"captured\",\"content_hash\":\"69c268ffc3b739ef3cddf708a9e9ffcdb360ead5e61f9d3e544b7cef55a7b681\",\"origin\":\"e48406173ab889163527e5f734bd3c02ac1f47d8c30538903b9326fd886ffe31\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[],\"operation\":\"wai.neural.int_hyper\",\"oversight\":null,\"record\":null,\"sig\":\"6a098643b18a49fad29ee82395a5d4dfe51a0074fc213947cb09c3010e9ffc8bc02cda5871fdc8dab487390f3ecd50c12c4a06e735e5fc82b1053d1a5f015206\",\"signer_pubkey\":\"ff703397c2545c095bb3a790fde7399961c3076bbefcac542792dc4c00acdd6c\",\"source\":\"digitalCapture\",\"step\":\"convert\",\"subject\":{\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"type\":\"object\"}}", - "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"super-resolution\",\"height\":3,\"input_samples\":\"339f6928d2e8141e1f57e78de7d01a757773f51221b69c8be91e57ae996adf7f\",\"output_samples\":\"e9aebdaebbd8ad1ef951b3d2d0cc62272cfcd73f1b69d11363d361826f458b2d\",\"params\":{\"scale\":2},\"tier\":\"attest\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[{\"digest\":\"e2ea8c07e5e2e2c846c97de6f5f79ab21593563603420e70736ab7f22ca451d4\",\"type\":\"trained\"}],\"operation\":\"super-resolution\",\"oversight\":null,\"record\":null,\"sig\":\"0cf70135dcca61696a1cf36b7dfff01475d0730495fad8c68e11a7b5d7eb62fff2be5345897dca3cb5c9b1ff0ca1ef7dde333a5d1dc0cabe7d34c246ce4b1f08\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"d4b33e2b9bad56645fcf0b7856b9009fecd2b4e5e587eeb791a0da87daa601ce\",\"type\":\"object\"}}" + "{\"class\":\"assistive\",\"evidence\":{\"derivation\":\"super-resolution\",\"height\":3,\"input_samples\":\"6ee5006f705630e934216406a83b76d1115f02ce90970b633f68abede9a5d484\",\"output_samples\":\"e9aebdaebbd8ad1ef951b3d2d0cc62272cfcd73f1b69d11363d361826f458b2d\",\"params\":{\"scale\":2},\"tier\":\"attest\",\"width\":4},\"inputs\":[{\"class\":\"reconstructed\",\"content_hash\":\"5c9d10e4264fb166fafb81b5dc92f1e0aff2be288d2242dbd16c466d42c586f2\",\"origin\":\"4e09c93646b7a60b0321ca20646718f23ab4ea2712f7608d174544ac9c9a2cfb\",\"source\":\"digitalCapture\"}],\"issued_at\":\"2026-10-21T09:00:00Z\",\"kind\":\"wai-origin\",\"media\":\"image\",\"models\":[{\"digest\":\"e2ea8c07e5e2e2c846c97de6f5f79ab21593563603420e70736ab7f22ca451d4\",\"type\":\"trained\"}],\"operation\":\"super-resolution\",\"oversight\":null,\"record\":null,\"sig\":\"8f3ea73024ea875df6ec23ea0f79b33e760ebb2036b356ad6d2f8813a620b07ddfc27d8e6d2ece330a82db3d5df571c80b3b32880a9bb8e480d12bce3d3e130b\",\"signer_pubkey\":\"08af3636c89e55c66c0498aacb199aeb6b3e3f3fd25fc4cf42b699a4b86f1070\",\"source\":\"algorithmicallyEnhanced\",\"step\":\"assist\",\"subject\":{\"content_hash\":\"d4b33e2b9bad56645fcf0b7856b9009fecd2b4e5e587eeb791a0da87daa601ce\",\"type\":\"object\"}}" ], "markings": [], "detections": [], diff --git a/wai/wai-rs/src/synthesis/corpus.rs b/wai/wai-rs/src/synthesis/corpus.rs index 382580e07..6be2910d2 100644 --- a/wai/wai-rs/src/synthesis/corpus.rs +++ b/wai/wai-rs/src/synthesis/corpus.rs @@ -101,8 +101,9 @@ struct Objects { fn objects() -> Objects { let photo_px = bytes("photo", W * H * 3); - // The codec's reconstruction: the photo with its low bits cleared. - let coded_px: Vec = photo_px.iter().map(|b| b & 0xF8).collect(); + // The codec's reconstruction: the photo quantised to multiples of 3, so that both the + // derivations' roundings decide some of their samples. + let coded_px: Vec = photo_px.iter().map(|b| b - b % 3).collect(); let up_px = upscale2(&coded_px, W, H); let dn_px = denoise3(&coded_px, W, H); let png = |s: &str| envelope(&manifest("image", "replicate", "wai.image.png"), &bytes(s, 48)); -- 2.54.0 (Apple Git-157) From 0c4e04f6e886fe5391bf3a4512a97fcd41963075 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 07:24:54 -0400 Subject: [PATCH 7/9] =?UTF-8?q?joule-moq-ingest:=20a=20grant=20whose=20ai?= =?UTF-8?q?=20attribute=20is=20not=20a=20model=20digest=20is=20grant=5Finv?= =?UTF-8?q?alid=20(live-rooms=20=C2=A74.4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rooms corpus gained grant_ai_malformed with the synthesis slice; the gateway's own grant reader now refuses it as wai-rs does. Co-Authored-By: Claude Opus 5.5 --- joule-code/joule-code-rs/crates/joule-moq-ingest/src/room.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/joule-code/joule-code-rs/crates/joule-moq-ingest/src/room.rs b/joule-code/joule-code-rs/crates/joule-moq-ingest/src/room.rs index 4091edf32..ce3c4588f 100644 --- a/joule-code/joule-code-rs/crates/joule-moq-ingest/src/room.rs +++ b/joule-code/joule-code-rs/crates/joule-moq-ingest/src/room.rs @@ -1021,6 +1021,11 @@ impl Grant { return bad; } let Some(attrs) = read_attrs(&attrs) else { return bad }; + // live-rooms §4.4: `ai`, marking an AI participant, is a model's digest as 64 lowercase + // hexadecimal digits. + if attrs.iter().any(|(k, v)| k == "ai" && !(v.len() == 64 && v.bytes().all(|c| c.is_ascii_digit() || (b'a'..=b'f').contains(&c)))) { + return bad; + } // Step 6. if exp - nbf > MAX_GRANT_S { return Err(Reason::GrantTooLong); -- 2.54.0 (Apple Git-157) From f733e288544547e35ec4f17632784afe01c3f97b Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 07:43:31 -0400 Subject: [PATCH 8/9] registry export: regenerated after merging main (live provenance + synthesis) Co-Authored-By: Claude Opus 5.5 --- wai/registry/capabilities.json | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/wai/registry/capabilities.json b/wai/registry/capabilities.json index 0e28a7a91..295e75805 100644 --- a/wai/registry/capabilities.json +++ b/wai/registry/capabilities.json @@ -756,6 +756,20 @@ "edition": null, "registered": "2026-10-20" }, + { + "capability": "wai.media.synthesis", + "kind": "record", + "status": "registered", + "media": null, + "role": null, + "tier": null, + "licence": null, + "replicate": null, + "sets": [], + "spec_section": "§5 Interactive, temporal & computed content", + "edition": null, + "registered": "2026-10-21" + }, { "capability": "wai.mesh.draco", "kind": "payload", @@ -2298,6 +2312,13 @@ "section": "§10.2", "status": "defined" }, + { + "name": "wai-detection", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§7.3", + "status": "defined" + }, { "name": "wai-key-status", "kind": "document-kind", @@ -2305,6 +2326,20 @@ "section": "§7.9", "status": "defined" }, + { + "name": "wai-label-policy", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§10.1", + "status": "defined" + }, + { + "name": "wai-marking", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§7.2", + "status": "defined" + }, { "name": "wai-module-grant", "kind": "document-kind", @@ -2319,6 +2354,20 @@ "section": "§8.4", "status": "defined" }, + { + "name": "wai-origin", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§3.1", + "status": "defined" + }, + { + "name": "wai-player", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§10.3", + "status": "defined" + }, { "name": "wai-receipt-index", "kind": "document-kind", @@ -2333,6 +2382,13 @@ "section": "§4.1", "status": "defined" }, + { + "name": "wai-review", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§9.1", + "status": "defined" + }, { "name": "wai1", "kind": "codecs-prefix", -- 2.54.0 (Apple Git-157) From 96095b511d2a5b6928d3ed0726353a1452b46ab2 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 10:23:26 -0400 Subject: [PATCH 9/9] registry export: regenerated after merging main (deriving edge + synthesis) Co-Authored-By: Claude Opus 5.5 --- wai/registry/capabilities.json | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/wai/registry/capabilities.json b/wai/registry/capabilities.json index b7996ab2a..e798442de 100644 --- a/wai/registry/capabilities.json +++ b/wai/registry/capabilities.json @@ -770,6 +770,20 @@ "edition": null, "registered": "2026-10-20" }, + { + "capability": "wai.media.synthesis", + "kind": "record", + "status": "registered", + "media": null, + "role": null, + "tier": null, + "licence": null, + "replicate": null, + "sets": [], + "spec_section": "§5 Interactive, temporal & computed content", + "edition": null, + "registered": "2026-10-21" + }, { "capability": "wai.mesh.draco", "kind": "payload", @@ -2319,6 +2333,13 @@ "section": "§5.1", "status": "defined" }, + { + "name": "wai-detection", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§7.3", + "status": "defined" + }, { "name": "wai-key-status", "kind": "document-kind", @@ -2326,6 +2347,20 @@ "section": "§7.9", "status": "defined" }, + { + "name": "wai-label-policy", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§10.1", + "status": "defined" + }, + { + "name": "wai-marking", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§7.2", + "status": "defined" + }, { "name": "wai-module-grant", "kind": "document-kind", @@ -2340,6 +2375,20 @@ "section": "§8.4", "status": "defined" }, + { + "name": "wai-origin", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§3.1", + "status": "defined" + }, + { + "name": "wai-player", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§10.3", + "status": "defined" + }, { "name": "wai-receipt-index", "kind": "document-kind", @@ -2354,6 +2403,13 @@ "section": "§4.1", "status": "defined" }, + { + "name": "wai-review", + "kind": "document-kind", + "document": "extensions/synthesis.md", + "section": "§9.1", + "status": "defined" + }, { "name": "wai1", "kind": "codecs-prefix", -- 2.54.0 (Apple Git-157)