open-standards/wai/live-conformance/stitched/README.md
Transaction Science 428d1601dd wai: stitched presentation — a verification states each record's ad region and share
A falsification moved a squeeze's side band onto the window's side and the corpus still
verified: the share is the same area, and no fact stated the rectangles. Each accepted record
now states its frame's share and its ad region's rectangles (live-compose §5.9), so wai-rs and
stitched_presentation_verify.py must agree on the region, not only its area; the Python
verifier gains the matching mutation.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 21:50:17 -04:00

8.8 KiB
Raw Permalink Blame History

live-conformance/stitched — stitched presentation

The corpus of extensions/stitched-presentation.md §10: streams with ads another party stitched into their segments or served as interstitials; the manifests the players read (HLS Media Playlists, an asset list, an MPD); the ad servers' responses (VAST documents and a form-1 wai.video.ssai receipt); the WSI1 pod-input record each break's signer signed; the WSC1 claims the players signed, good ones and a control of each check; what a verifier states of them.

Presentation equivalence. For each vector, a conforming verifier given its records and claims, the files its manifests, responses and receipts name, and the corpus's creatives, issuers and signers (§7.1), states exactly:

  • record_facts, each record in the order given: its digest (null when it does not read) and its verdict, accepted or a reason; for an accepted record, response_checked, its ads (each slot, its own_claims by index, presentations, verdict, quartiles, presented_spend_micros and stated_quartile), its presented_spend_micros, its stated_spend_micros, and its frame's share_bp and ad region (rectangles [x, y, w, h], null for the full picture);
  • claim_facts, each claim in the order given: its digest, its verdict, presented or a reason, the record and slot it names (from §7.3 step 4 on), and, for an own claim, the quartile it reaches, its run_ms and its viewable_ms;
  • spend, the presented spend by currency.

record_about and claim_about say what each record and claim is, and are not checked. The files a vector's bound names are documents a record binds by digest and no verifier reads (a winning bid, a VMAP response); they are here so that anyone recomputes the digests. A verifier that states another verdict or fact does not conform.

Provenance. Generated here: wai_stitched_presentation vectors gen writes every file from streams authored in code, from fixed seeds. Each session key is SHA-256 of wai:stitched-presentation/corpus/player, a zero byte and its number (players lists their public keys); each signer's key the same with signer (signers 1 and 2 are the trusted ones, signers; signer 9 is not); each media object's content hash SHA-256 of a fixed label and two numbers; each token's nonce SHA-256, and its salt SHA-384, of a fixed label and its number. The attestation issuer's key is the test key RFC 9578 Appendix A.2 publishes, as in ../presentation/. Every playlist, asset list, MPD, VAST document, bid document and receipt is a fixture written for its case under example domains; none was fetched from a service. Ed25519, BLAKE3 and RSASSA-PSS over a given salt are deterministic, so every file is the same bytes on every machine. The generator asserts that each record and claim gets the verdict it was written to have and that the corpus gives every reason of §9, before it writes. No recorded data.

Layout

  • vectors.json: creatives, each its objects (content_hash, duration_ms); issuers; signers (Ed25519 public keys, hexadecimal); players (informative); and vectors.
  • NAME.*: each vector's documents: .m3u8 Media Playlists, .assets.json an asset list, .mpd an MPD, .vast.xml VAST 4.3 responses, .vmap.xml a VMAP response, .bid.json a winning bid's document, .receipt.json a form-1 receipt in its JSON carriage (§4.5).

The vectors:

  • hls_stitched: two viewers' personalised playlists, each ad under a date range whose X-AD-ID carries its AdServingId; a record per viewer priced from its VAST Pricing (CPM 25.00, 18.00 and 30.00 USD). Presented: viewer 1's two ads (the second to its third quartile) and viewer 2's first. Viewer 2's second ad was fetched ahead of the break and never presented: claim_absent, no spend. Record controls: pod_signer_unknown, pod_response_mismatch (a price the response does not give; a universal creative identifier it does not carry), pod_duplicate, pod_input_malformed, bad_signature. Claim controls: claim_duplicate, claim_key_unbound, claim_session_unbound, claim_no_decision, claim_manifest_unknown, claim_marker_absent, stitched_claim_malformed (truncated; a signed skip with no resumption), bad_signature.
  • hls_interstitial: one shared playlist with an interstitial date range, and the asset list a viewer was served; records under the cross-media rule with an audible run and attestation, ad-i1 at a winning bid's price (its document bound by digest), ad-i2 at CPM 20.00. Presented: viewer 11's two, attested on a television. Controls: attestation_absent, attestation_invalid (another presentation's token).
  • squeezeback: a squeeze on a DASH channel, the program in a 1440 × 810 window in the bottom right corner and the ad in the L around it (4375 basis points of the picture), marked by an event whose messageData carries the ad's AdServingId; one shared record. Presented: viewers 21, 22 (70% of the region in view) and 23 (to the midpoint): three presentations. Controls: claim_frame_mismatch, claim_not_viewable, claim_duplicate, claim_asset_mismatch.
  • double_box: a double box on an HLS channel, its record in EUR at a price the signer states, its VMAP response bound and not read. Presented: viewer 31 over the ad's box, viewer 32 over the player's area.
  • form1_prefetch: a stitcher's own decision sealed in a form-1 receipt whose beacons counted ad-f2 as started when its first segment was fetched ahead of the break; viewer 41 left after ad-f1. Stated: quartiles 5 and 1, 5 000 000 µ-units; presented: ad-f1 alone, 3 000 000. Control: pod_response_mismatch (the same receipt at another price).

Running

cd ../../wai-rs
cargo run --no-default-features --features stitched_presentation --bin wai_stitched_presentation -- vectors verify ../live-conformance/stitched
cargo run --no-default-features --features stitched_presentation --bin wai_stitched_presentation -- vectors gen    ../live-conformance/stitched   # regenerate
cargo test --no-default-features --features stitched_presentation --test stitched_presentation
cd ../live-conformance
python3 -I stitched_presentation_verify.py stitched
node --test ../../joule-code/joule-code-rs/crates/joule-moq-web/web/ads/stitched.test.mjs

vectors verify and tests/stitched_presentation.rs require every file to regenerate byte for byte, no other file but this one to be there, and wai-rs to give every vector its facts; the test also requires §9 to list exactly the codes the reference adds. stitched_presentation_verify.py is the second implementation, written from the extension's text and sharing no code with wai-rs: it reads every manifest, response, record and claim itself (with the spans' quartile and run and RSASSA-PSS of ad_presentation_verify.py, a form-1 receipt's leaves of live_ads_verify.py and the ad frame of live_compose_verify.py), requires every reason to appear, then mutates copies of the corpus, one defect each, each of which must fail, and drops each of its own checks in turn, each weakened verifier of which the corpus must catch. The browser client's reference producer requires the corpus's claims from the corpus's manifests and observation sequences. Pass = an exit status of 0 from each.

The tool's inputs

wai_stitched_presentation verify <bundle.json> (and POST /v1/verify of the service) reads one JSON object:

member value
records the pod-input records, hexadecimal
claims the claims, hexadecimal, at most 2^20
manifests the manifests' texts, as strings
responses the VAST and VMAP documents' texts, as strings
receipts form-1 receipts in their JSON carriage (§4.5), as objects
creatives as vectors.json's
issuers as vectors.json's
signers the accepted signers' keys, hexadecimal

and prints record_facts, claim_facts and spend as this corpus states them, with the capability. wai_stitched_presentation sign-pod <draft.json> --key <seed-file> reads a draft of a record: the fields of §4.1 by name (carriage, session in hexadecimal or absent for a shared break, avail_id, avail_ms, response_format, response_file (a path beside the draft whose SHA-256 is taken) or response (hexadecimal), id_kind, id_attr, currency, exponent, frame (its fields, or absent for the full picture) and requirement (require, rule, token_type, issuer)), and ads, each with marker as its fields (id and start for carriage 1; and index for 2; period, scheme, value and id for 3), ad_id, serving_id, creative_id, registry, creative_hash, duration_ms, price_micros, price_source and evidence. It refuses a draft that breaks §4.4, and prints the signed record in hexadecimal.