| Filename | Latest commit message | Latest commit date |
|---|---|---|
The change of wai/ad-consent-supply-chain (44d5f2264..110c53553, 2 commits), on the fix branch: its changelog entries as files of wai/changes/, its wai-conformance.yml edits as step files, corpus files and test tags, and every other file as the branch has it. - f8fe39cba WAI: an ad decision's consent signal and supply chain, bound into what the decider signs - 110c53553 WAI: a GPP string links what carries it; a chain per impression; sellers files read exactly Co-Authored-By: Claude Opus 5.5 <[email protected]> |
||
| .. | ||
| authorised_read_liberally.m3u8 | ||
| chapters.m3u8 | ||
| child_room.m3u8 | ||
| consent_and_chain.m3u8 | ||
| live_break.m3u8 | ||
| mid_roll.m3u8 | ||
| post_roll.m3u8 | ||
| README.md | ||
| sellers_read_by_grammar.m3u8 | ||
| supply.channel.example.ads.txt | ||
| supply.channel.example.app-ads.txt | ||
| supply.exchange.example.sellers.json | ||
| supply.gateway.example.sellers.json | ||
| supply.lone-surrogate.sellers.json | ||
| supply.nan.sellers.json | ||
| supply.not-sellers.json | ||
| supply.not-utf8.app-ads.txt | ||
| supply.passed-over.app-ads.txt | ||
| supply.sellers-twice.sellers.json | ||
| supply.ssp.example.sellers.json | ||
| supply.too-deep.sellers.json | ||
| vectors.json | ||
live-conformance/ads — live ads
The corpus of extensions/live-ads.md §11: rooms whose cue
events open ad breaks, each a room (its WRD1 descriptor, WRG1 grants, key-log head or none,
and each principal's WEV1 chain, the break annotations WAB1 among them), its WAP1 ad
policy, the presentation time of t0 on the DASH Period (pt0_ms), and the break-form
wai.video.ssai receipts of its viewers, each with the pod its decision ran over; what a
verifier states of them; and each accepted room's HLS tags, byte for byte. Receipts of version
2 bind the consent signal their decision was made under and the supply chain of each impression
(§8.6, §8.7), and the vectors that hold them name the sellers files and authorised-sellers
files they are checked against.
Ads equivalence. For each vector, a conforming ads verifier given its descriptor,
grants, key_log, chains, policy, pt0_ms, receipts and pods, and the files its
supply_files names when it has that member, each as its bytes (§9.1), reaches the outcome the
vector states
(§9.4):
refuse: thereasonand where,at:["room", …]with the room verifier's own place (live-rooms §9.1),["policy"],["record", c, i]for recordiof chainc, or["receipt", k];gap: the room'sgaps, eachprincipal,firstandlast;accept: theroomid; thebreaks(§4.3), each itsdigest,idandevent_id(§4.4),author,cue_id,t,type(outorbreak),placement(midorpost),planned_ms,avail_ms,duration_ms,closed_byandend(nullwhile it is open), and itshls_list_urianddash_list_uri(§7.2, §7.3); thechapters, eachdigest,tandduration_ms;hls, the file holding the room's HLS tags;dash, the Period's event streams (§6.3);dash_open, each out's event as written before its in, by the out's index among thebreaks;receipt_facts, each receipt'sidentity(§8.4), theasset_listit binds as the verifier writes it, and itsspend_micros, and, for a receipt of version 2, itsconsent(gpp,gpp_sid,child_directed) and itssupply_chains, one for each impression, eachnullor itscomplete, itsnodes(eachasi,sid,sellers_checked,seller_typeandrelationship) andauthorised_checked; andspend_micros, the spend of every receipt together.
Each tag file, NAME.m3u8, is the HLS tags of vector NAME (§5.1 to §5.3), one tag a line in
the order §5.4 gives, each line ended by a line feed. It is the date ranges of the room's Media
Playlists, not a whole playlist: the segments are the room's tracks.
A verifier that reaches another outcome, reason, place, break, tag byte, event byte, asset-list byte, identity or spend does not conform.
Provenance. Generated here: wai_live_ads_vectors gen writes every file from rooms
authored in code, from fixed seeds. Each principal's key is SHA-256 of a fixed label and the
principal's index; each creative's hash and each session id likewise; every time is a
constant (the rooms' t0 is 2026-10-16T00:00:00Z); every origin and base URI is an
.example name (RFC 2606); and Ed25519 signing is deterministic (RFC 8032), so every file is
the same bytes on every machine. The GPP string is the example the GPP string's text gives for
a service reached by a URL, with its section 2; the sellers files and the authorised-sellers
files are fixtures written for their cases under .example names, none fetched from a service.
The generator asserts that each vector reaches the outcome it
was written to have, and that the corpus gives every reason of §10, before it writes. No
recorded data.
Layout
vectors.json:principals(the keys, by name, informative) andvectors.NAME.m3u8: the HLS tags of each accepted room.supply.NAME: the sellers files (supply.ssp.example.sellers.json,supply.exchange.example.sellers.json, andsupply.gateway.example.sellers.json, which reads only by RFC 8259's grammar outside what §8.7 reads), the channel's authorised-sellers files (supply.channel.example.app-ads.txt, andsupply.channel.example.ads.txt, whose records need each reading rule of §8.7), and files that do not read or authorise:supply.not-sellers.json(no sellers),supply.too-deep.sellers.json,supply.lone-surrogate.sellers.json,supply.sellers-twice.sellers.json,supply.nan.sellers.json,supply.not-utf8.app-ads.txtandsupply.passed-over.app-ads.txt. They are read as bytes: some end lines with CR LF or a CR alone, and one is not UTF-8.wai/.gitattributeskeeps every line ending as it is.
Every room has a producer, whose grant permits cue, meta and chat; a viewer, whose grant
permits chat; and the decider, whose grant permits no event and carries the room's ad budget,
2 minor units of EUR (2 000 000 millionths), and which never joins. The clock admits the
producer and the viewer at the room's start.
The rooms:
live_break: an out at 60 s with no planned duration and its in at 90 s, under a policy whose open avail is 30 s and whose preload is 4 s. One viewer, present at the break's start, is filled with two 15 s ads from an HLS asset list (its decision energy model-based); a second joins 12 s into the break and is filled with what remains, one ad, from a DASH list MPD (its decision energy an on-chip counter's reading). No pre-roll.mid_roll: a 30 s break, annotated mid-roll, under a policy that restricts skipping and jumping, snaps both ways and offers a skip control after 5 s, withpt0_ms5000. One viewer's pod has two ads at one price, ordered byad_id, and three fill the avail; another viewer's only ad costs more than its funds, so its fill is empty and its DASH list MPD is no bytes.post_roll: two chapters, a 15 s mid-roll at 30 s, and a 20 s break annotated post-roll at 120 s; one viewer filled in both breaks, another in the post-roll only.chapters: a chapter of a stated length and one of none, then an out with a planned duration of 15 s and its in, which states no duration.gap:live_break's room as a subscriber holds it when the out was not delivered.- refused, one defect each:
cue_kind_not_permitted(refused by the room,kind_not_permitted),policy_slots_zero,policy_wrong_room,policy_bad_signature,decider_grant_unknown,decider_grant_revoked(a room whose key log revokes the decider grant,grant_revoked),annotation_placement,annotation_on_out,cue_after_post_roll,overlapping_breaks,open_break_no_avail,break_of_no_duration,in_without_out,in_duration_mismatch,receipt_extra_member,receipt_wrong_room,receipt_bad_signature,receipt_quartile_sixand, for the other rules of §8.5 each broken alone,receipt_format_three,receipt_work_below_fill,receipt_figure_without_class,receipt_below_resolutionandreceipt_over_its_ceiling(allreceipt_invalid),receipt_not_decider,receipt_grant_mismatch,receipt_names_the_in,decider_grant_expired(outside_grant_window),pod_mismatch,decision_mismatch,asset_list_mismatch,receipt_duplicateandfunds_exceeded(a budget of 1 minor unit, and two viewers each billed 600 000 millionths). consent_and_chain:live_break's room under a policy of version 3. The first viewer's receipt states the GPP string its player gave, with section 2, and a chain for each of its two ads, which answered two requests: a complete chain from the channel's own account atssp.example(pub-1001,PUBLISHER,DIRECT) throughexchange.example(the SSP's accountssp-77,INTERMEDIARY,RESELLER), and a complete chain of one node, the channel's own account at the exchange (pub-2001,PUBLISHER,DIRECT), each checked against the sellers files and the channel's app-ads.txt. The joining viewer's states no string, a child-directed request, and an incomplete chain of one node whose sellers file the verifier does not hold: unchecked.child_room: a 15 s break in a room whose policy marks it as directed to children; the receipt states the request child-directed and no chain for its ad.authorised_read_liberally: the first viewer's two ads answered one request, so its receipt states one chain twice: four nodes, each authorised bysupply.channel.example.ads.txtonly when it is read by §8.7's rules (a record of four fields; one whose advertising system is in other letter cases and that ends with CR LF; one whose relationship is in lower case and that ends with a CR alone; one with an extension field).sellers_read_by_grammar: the first viewer's first ad sold through the gateway's sellerpub-4001, whichsupply.gateway.example.sellers.jsonlists only when what §8.7 does not read is read by RFC 8259's grammar alone: a number past binary64's range, one of 400 digits, escapes of lone surrogates in members skipped, a member name escaping a character with a pair of surrogates, a member nested 64 deep with the file's object, and a seller id that escapes one of its characters. Its second ad states no chain.- refused, one defect each:
receipt_consent_altered,receipt_child_flag_cleared,receipt_chain_alteredandreceipt_chains_swapped(a GPP string, a child-directed flag, a first node's seller id and the order of the two chains changed after signing, eachbad_signature);receipt_version_three,receipt_v1_with_consentandreceipt_v2_without_chains(receipt_malformed);receipt_gpp_empty_section,receipt_gpp_not_url_safe,receipt_sections_without_string,receipt_section_repeatedandreceipt_child_flag_two(consent_invalid);receipt_chain_ver,receipt_chain_complete_two,receipt_asi_url,receipt_authorised_file,receipt_authorised_domainandreceipt_chains_count(supply_chain_invalid);receipt_without_terms(a receipt of version 1 under a policy of version 3,request_terms_absent);child_flag_unset(child_directed_unset);sellers_file_malformed,sellers_file_too_deep,sellers_file_lone_surrogate,sellers_file_sellers_twice,sellers_file_nanandauthorised_file_not_utf8(supply_file_malformed);seller_unlisted(supply_seller_unlisted);seller_unauthorised(a chain stated complete whose first node is a reseller's account),seller_absent_from_authorisedandauthorised_record_passed_over(a node named only in a comment and in a line of five fields) (supply_seller_unauthorised); andpolicy_audience_bit(ad_policy_malformed).
Running
cd ../../wai-rs
cargo run --no-default-features --features live_ads --bin wai_live_ads_vectors -- verify ../live-conformance/ads
cargo run --no-default-features --features live_ads --bin wai_live_ads_vectors -- gen ../live-conformance/ads # regenerate
cargo test --no-default-features --features live_ads --test live_ads
cd ../live-conformance
python3 -I live_ads_verify.py ads
wai_live_ads_vectors verify and tests/live_ads.rs require every file to regenerate byte for
byte, no other file but this one to be there, and wai-rs to give every case its outcome; the
test also requires §10 to list exactly the reasons the reference reports, and the generator
that the corpus gives each of them. live_ads_verify.py is the second implementation, written
from the extension's text and sharing no code with wai-rs: it verifies each room with
live_rooms_verify.py, writes every tag, event and asset list itself, verifies every receipt's
signature with WAI's strict Ed25519 rule and computes its BLAKE3 hashes from the specification,
reads every receipt's request terms and every supply file itself, as bytes, reads §10 from the
extension, gives every case the same outcome, requires every reason of §10 to appear, and then mutates
copies of the corpus, one defect each, each of which must fail.
Pass = an exit status of 0 from each.