Some checks failed
wai-web wasm32 / does this change touch it (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-quantum studio and desktop / does this change touch it (pull_request) Has been cancelled
wai-quantum studio and desktop / studio · browser (pull_request) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-pack / wai-pack · does this change touch it (pull_request) Has been cancelled
wai-pack / wai-pack · linux (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Has been cancelled
WAI energy receipts / does this change touch it (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI conformance / does this change touch it (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
standards workspaces build + test / which workspaces this change touches (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (pull_request) Has been cancelled
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-relay, every target (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <[email protected]>
677 lines
43 KiB
Markdown
677 lines
43 KiB
Markdown
# WAI Extension: Stitched Presentation
|
||
|
||
> Status: Draft. Registers the record `wai.video.stitched_presentation`: a player's signed
|
||
> claim, `WSC1`, that it presented one ad another party wrote into the stream, either stitched
|
||
> into the segments of an HLS or DASH presentation or served as an HLS interstitial, beside the
|
||
> **pod-input record**, `WSI1`, that the stitcher or a proxy in front of the ad server signs for
|
||
> each ad decision. A claim names its ad by the **marker** the stream carries for it (an
|
||
> `EXT-X-DATERANGE` tag, an asset of an interstitial's asset list, or an `Event` of a DASH
|
||
> `EventStream`) and the ad identity that marker carries, in the manifest whose SHA-256 it
|
||
> states; it carries ad-presentation's spans of visible and audible play, its viewability rules
|
||
> and quartile, and its device attestation; and, for an ad shown in a composited frame (a
|
||
> squeeze or a double box of [live-compose](live-compose.md) §5.9), the share of the picture the
|
||
> ad's region holds. The pod-input record states the digest of the ad server's response, the
|
||
> ads it chose with their markers, creatives and prices, the avail, the frame and the
|
||
> measurement terms. A verifier recomputes, from the claims that verify, each ad's
|
||
> presentations, the quartiles they reached and the spend that counts; the choice stays the ad
|
||
> server's. No new transport. The reference implementation is the `stitched_presentation`
|
||
> module of `wai-rs` (feature `stitched_presentation`); the corpus is
|
||
> [`live-conformance/stitched/`](../live-conformance/stitched/), and its second implementation
|
||
> is `live-conformance/stitched_presentation_verify.py`.
|
||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||
>
|
||
> Texts read for this revision, each at its source:
|
||
>
|
||
> - *Video Ad Serving Template (VAST)* 4.3, December 2022: §3.1 to §3.4.4, §3.4.8, §3.6 to
|
||
> §3.7.1 and §3.8 to §3.8.1, beside the sections [ad-presentation](ad-presentation.md) read.
|
||
> - *Server-Side Ad Insertion and OTT Guidance*, August 2021, §1.2, §2 and §3.
|
||
> - For HLS and DASH, the texts [live-ads](live-ads.md) read, at the revisions it names:
|
||
> `draft-pantos-hls-rfc8216bis-22` §4.2 (attribute lists), §4.4.5.1 (`EXT-X-DATERANGE`) and
|
||
> Appendix D (interstitials and the asset list); ISO/IEC 23009-1:2026's MPD schema (its
|
||
> `EventStream` and `Event`). Nothing here depends on a part of them live-ads did not read.
|
||
> - RFC 8259 (JSON) and XML 1.0, for the subset of §3.5.
|
||
>
|
||
> A section number of one of these texts is that revision's.
|
||
|
||
## 1. Scope
|
||
|
||
In scope: the marker of an ad in a stream, its body and digest, and how each is read from a
|
||
playlist, an asset list or an MPD (§3); the pod-input record, its rules and signature, and a
|
||
form-1 `wai.video.ssai` receipt as its response (§4); the stitched claim, its session, its
|
||
rules, its signature and its attestation challenge (§5); composited frames (§6); verification:
|
||
each record's and each claim's verdict, each ad's presentations and quartiles, and the spend
|
||
that counts, by currency (§7); and the reference producer, the tool and the service (§8).
|
||
|
||
Out of scope: a transport; how a player's session key reaches the signer (§4.6 says what the
|
||
signer does with it, not how it arrives); the ad decision itself, which stays the ad server's;
|
||
how a stitcher conditions a creative into segments; the contents of a winning bid's document
|
||
and of a VMAP document, which a record binds by digest and this revision does not read; the
|
||
SCTE-35 messages a stream may carry; a billing period, an invoice or a payment, which an
|
||
operator's system makes from what §7 states; and the detection of invalid traffic, which stays
|
||
statistical and the measurer's (ad-presentation §13).
|
||
|
||
Relationship to the core spec: `wai.video.stitched_presentation` 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 [ad-presentation](ad-presentation.md): that extension's claim names an
|
||
impression of a break-form receipt of a room; it puts the stitched form of `wai.video.ssai`
|
||
out of its scope, since such receipts name no session of a room. This extension brings stitched
|
||
and interstitial ads into scope with a claim of its own. It takes from ad-presentation, by
|
||
reference: the creative digest (§4 there); the span and its rules (§5.2, §5.4 rules 4 to 6);
|
||
the viewability rules, the qualifying run and the quartile (§6); the token, its challenge and
|
||
its issuer's verifier (§7); and the presentation fields of a policy (§8), here carried in the
|
||
record. It changes nothing in ad-presentation.
|
||
|
||
Relationship to [live-ads](live-ads.md) and `wai.video.ssai`: a room's breaks keep live-ads'
|
||
policy and break-form receipts. A stitcher that runs `wai.video.ssai`'s own deterministic
|
||
decision seals a form-1 receipt as before; a record whose response is that receipt (§4.5) lets
|
||
a verifier set the receipt's stated quartiles and billable spend beside the ones the players'
|
||
claims give. Every receipt of either form keeps its bytes.
|
||
|
||
Relationship to [live-compose](live-compose.md): a record's frame (§4.1) is live-compose
|
||
§5.9's ad frame, and the ad's region and its share are that section's.
|
||
|
||
## 2. Terms
|
||
|
||
- **Stream**: an HLS presentation or a DASH presentation, live or on demand.
|
||
- **Stitcher**: the server that writes ads into a stream: into its segments (server-side ad
|
||
insertion), or as interstitials a player fetches (server-guided ad insertion).
|
||
- **Ad server**: the party whose response decides which ads fill a break.
|
||
- **Response**: the ad server's answer, as the signer received it: a VAST document, a VMAP
|
||
document, or a form-1 `wai.video.ssai` receipt (§4.5).
|
||
- **Signer**: the key that signs a pod-input record: the stitcher's, or that of a proxy that
|
||
forwards the stitcher's ad requests to the ad server and their responses back.
|
||
- **Break**, **avail**: a span of the stream that ads fill, and its duration.
|
||
- **Manifest**: a document a player reads a marker from: a Media Playlist, an asset list or an
|
||
MPD (§3).
|
||
- **Marker**: the fields that place one ad in a stream (§3).
|
||
- **Ad identity**: the string a marker carries for its ad (§3), 1 to 255 bytes of UTF-8.
|
||
- **Player**, **session key**, **creative**, **span**, **geometry**, **token**, **issuer**,
|
||
**basis point**: as in ad-presentation §2.
|
||
- **Record**: a pod-input record (§4). **Claim**: a stitched claim (§5).
|
||
- **Presented**: the verdict of a claim that passes every step of §7.3.
|
||
|
||
Integers are little-endian unless this text says otherwise. A digest is SHA-256 (FIPS 180-4),
|
||
32 bytes. Ed25519 is RFC 8032's, 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 is
|
||
`bad_signature`. A *short* field is a u8 length and that many bytes; a *long* field is a u16
|
||
length and that many bytes. A *text* field is a short field of 1 to 255 bytes of UTF-8.
|
||
|
||
## 3. Markers
|
||
|
||
### 3.1 The marker, its body and its digest
|
||
|
||
A marker is one of three kinds, by its **carriage**:
|
||
|
||
| carriage | where the stream carries it | its fields |
|
||
|---|---|---|
|
||
| 1 | an `EXT-X-DATERANGE` tag of a Media Playlist (§3.2) | the tag's `ID` and `START-DATE` |
|
||
| 2 | an asset of an HLS interstitial's asset list (§3.3) | the interstitial date range's `ID` and `START-DATE`, and the asset's index in its asset list |
|
||
| 3 | an `Event` of a DASH `EventStream` (§3.4) | the `id` of its `Period`, the `schemeIdUri` and `value` of its `EventStream`, and its own `id` |
|
||
|
||
A marker's **body** is its fields in that order, each a long field of its bytes as the
|
||
manifest writes them (an attribute value with its quotes removed; an XML attribute value with
|
||
its references replaced, §3.5), with an absent `Period` `id` or `EventStream` `value` empty;
|
||
and, for carriage 2, the asset's index as a u16 after the two long fields. A body of a carriage
|
||
**reads** when it is exactly those fields, with no byte after them.
|
||
|
||
The **marker digest** is SHA-256 over `wai:stitched-marker` (ASCII) and 0x01, the carriage (one
|
||
byte) and the body.
|
||
|
||
An HLS date range's `ID` names it within its playlist, and its `START-DATE` is fixed for the
|
||
`ID` (`draft-pantos-hls-rfc8216bis-22` §4.4.5.1); together they place an ad on the stream's
|
||
timeline, and stay the same across the reloads of a live playlist. A DASH event is named by its
|
||
scheme, its value and its `id` within its Period.
|
||
|
||
### 3.2 Carriage 1: a date range
|
||
|
||
A Media Playlist is read as its lines: its bytes are UTF-8, split at each line feed with a
|
||
carriage return before it dropped, and its first line is `#EXTM3U`. Each line that begins
|
||
`#EXT-X-DATERANGE:` is a **date range tag**, and the rest of the line its **attribute list**:
|
||
`NAME=VALUE` pairs separated by single commas, each name of `A`–`Z`, `0`–`9` and `-` and given
|
||
once; a value is a quoted string, `"` then bytes other than `"`, CR and LF then `"`, or else one
|
||
or more bytes other than `"`, `,` and white space. A tag whose list does not read so, or whose
|
||
`ID` or `START-DATE` is not a quoted string, names no ad. (These are §4.2's rules for an
|
||
attribute list, as live-ads reads them.)
|
||
|
||
An ad of carriage 1 is a date range tag that carries the attribute the record's `id_attr` names
|
||
(§4.1). Its marker is the tag's `ID` and `START-DATE`, and its ad identity is that attribute's
|
||
value: a quoted string's contents, or the value as written. A stitcher writes the ad identity
|
||
in an attribute of the date range of each ad it stitches; the attribute's name is the
|
||
stitcher's (a client attribute, `X-` and a name, by §4.4.5.1), and a record names it.
|
||
|
||
### 3.3 Carriage 2: an interstitial's asset
|
||
|
||
An **interstitial date range** is a date range tag (§3.2) whose `CLASS` is the quoted string
|
||
`com.apple.hls.interstitial` (Appendix D.2, as live-ads §5.1 writes it). <!-- allow-name: the interstitial date-range class, a protocol identifier -->
|
||
Its asset list is the JSON object (RFC 8259) Appendix D.2 defines: its `ASSETS` member is an
|
||
array of objects, each with a string member `URI` (live-ads §7.2). An ad of carriage 2 is the
|
||
asset at index *k* of the asset list a player was served for an interstitial date range. Its
|
||
marker is the date range's `ID` and `START-DATE` and *k*, and its ad identity is the asset's
|
||
`URI`.
|
||
|
||
The asset list is the manifest a claim of carriage 2 names (§5.1), since each viewer may be
|
||
served another one under the same date range; the playlist that carries the date range is held
|
||
beside it (§7.3 step 6).
|
||
|
||
### 3.4 Carriage 3: an event
|
||
|
||
An MPD is read as XML by the subset of §3.5. Its root element is `MPD`; each child `Period` of
|
||
the root, each child `EventStream` of a Period that has a `schemeIdUri`, and each child `Event`
|
||
of such an EventStream that has an `id` is considered. An ad of carriage 3 is such an Event
|
||
that carries the attribute the record's `id_attr` names. Its marker is its Period's `id`, its
|
||
EventStream's `schemeIdUri` and `value`, and its own `id`; its ad identity is the value of that
|
||
attribute.
|
||
|
||
### 3.5 The XML subset
|
||
|
||
An XML document (an MPD of §3.4, a VAST document of §7.2) is read over this subset of XML 1.0,
|
||
and one outside it does not read:
|
||
|
||
- its bytes are UTF-8 with no byte order mark;
|
||
- before and after its one root element there are only white space, comments and processing
|
||
instructions (an XML declaration among them); a document type declaration, or any other
|
||
markup that begins `<!` other than a comment or a CDATA section, is outside the subset;
|
||
- an element is a start tag, its content and its end tag with the same name, or an empty-element
|
||
tag; a tag's attributes each follow white space and are a name, `=` and a value in double or
|
||
single quotes holding no `<`, each name once;
|
||
- content is character data, CDATA sections, comments, processing instructions and elements,
|
||
nested at most 64 deep, the root counted as 1;
|
||
- in character data and attribute values, the references `<`, `>`, `&`, `"`,
|
||
`'`, `&#`*n*`;` (decimal) and `&#x`*h*`;` (hexadecimal) are replaced by the character
|
||
they name; any other `&` is outside the subset. An attribute value is otherwise taken as
|
||
written: no white space in it is normalised.
|
||
|
||
Elements and attributes are named by their **local name**: the part after the last `:`. An
|
||
element's **text** is its character data and CDATA children, joined in order, with white space
|
||
(space, tab, CR, LF) removed from both ends.
|
||
|
||
## 4. The pod-input record
|
||
|
||
### 4.1 Layout
|
||
|
||
| size | field | meaning |
|
||
|---|---|---|
|
||
| 4 | `magic` | `WSI1` (ASCII) |
|
||
| 1 | `version` | 1 in this revision |
|
||
| 1 | `carriage` | how the stream marks its ads, 1 to 3 (§3.1) |
|
||
| 16 | `session` | the session the decision was made for (§5.2); 16 zero bytes for a **shared** break, one every viewer of the stream is shown (§4.6) |
|
||
| short | `avail_id` | the break, as the signer names it: a text field |
|
||
| 4 | `avail_ms` | u32: the avail the ad request asked to fill, in milliseconds, at least 1 |
|
||
| 1 | `response_format` | 1 a VAST document; 2 a VMAP document; 3 a form-1 `wai.video.ssai` receipt (§4.5) |
|
||
| 32 | `response` | formats 1 and 2: SHA-256 of the response's bytes as the signer received them; format 3: the receipt's identity (its receipt hash) |
|
||
| 1 | `id_kind` | what an ad's `serving_id` is: 1 the `AdServingId` of its `InLine` (VAST 4.3 §3.4.3); 2 the `id` of its `Ad` (VAST 4.3 §3.3.2); 3 a form-1 impression's `ad_id` |
|
||
| short | `id_attr` | the attribute that carries the ad identity: for carriage 1 an HLS attribute name of `A`–`Z`, `0`–`9` and `-`; for carriage 3 an XML name (§4.4); for carriage 2 no bytes |
|
||
| 3 | `currency` | the prices' currency: an ISO 4217 alphabetic code, three of `A`–`Z` |
|
||
| 1 | `exponent` | its minor unit, as ISO 4217 lists it: 0 to 4 |
|
||
| 13 | `frame` | the frame the break is shown in (§4.3) |
|
||
| 4 + long | measurement terms | `presentation`, `viewability`, `token_type` (u16) and `issuer` (long), as ad-presentation §8 defines them |
|
||
| 1 | `n` | the ads' count, 1 to 16 |
|
||
| … | `ads` | `n` ads, §4.2 |
|
||
| 32 | `signer` | the signer's Ed25519 key |
|
||
| 64 | `sig` | §4.4 |
|
||
|
||
### 4.2 An ad
|
||
|
||
| size | field | meaning |
|
||
|---|---|---|
|
||
| 1 | `slot` | its place in the pod: 0, 1, 2, … in order |
|
||
| 32 | `marker` | its marker digest (§3.1) |
|
||
| short | `ad_id` | the ad identity its marker carries (§3): a text field |
|
||
| short | `serving_id` | its identity in the response, as `id_kind` names it: a text field |
|
||
| short | `creative_id` | its creative's universal identifier, a `UniversalAdId`'s text (VAST 4.3 §3.7.1), or no bytes |
|
||
| short | `registry` | that identifier's `idRegistry`; no bytes exactly when `creative_id` has none |
|
||
| 32 | `creative_hash` | the creative digest (ad-presentation §4) of the media objects the stream serves for it |
|
||
| 4 | `duration_ms` | u32: the sum of those objects' durations, at least 1 |
|
||
| 8 | `price_micros` | u64: the price of one presented impression, in millionths of a minor unit of `currency`, at most 10^12 |
|
||
| 1 | `price_source` | 1 the response's `Pricing` (VAST 4.3 §3.4.8) of model CPM, checked (§7.2); 2 a winning bid's price; 3 a price the signer states |
|
||
| 32 | `evidence` | for `price_source` 2, SHA-256 of the winning bid's document, which this revision binds and does not read; otherwise 32 zero bytes |
|
||
|
||
A price is per presented impression, so that the spend of §7.4 is a count of presentations
|
||
times a price. A CPM price *P* in a currency of exponent *e* is *P* × 10^(*e* + 3) millionths of
|
||
a minor unit per impression: *P* × 10^*e* minor units a thousand impressions, times 10^6 over
|
||
1000. CPM 25.00 in a currency of two decimals is 2 500 000.
|
||
|
||
### 4.3 The frame
|
||
|
||
| size | field | meaning |
|
||
|---|---|---|
|
||
| 1 | `kind` | 0 the full picture; 3 a squeeze; 4 a double box (live-compose §5.9) |
|
||
| 2 | `width` | u16: the picture's width in pixels |
|
||
| 2 | `height` | u16: its height |
|
||
| 1 | `corner` | live-compose §5.9's `corner`: a squeeze's program window's corner, 1 to 4; a double box's program side, 1 left, 2 right |
|
||
| 2 | `window_w` | u16: a squeeze's program window's width; otherwise 0 |
|
||
| 2 | `window_h` | u16: its height; otherwise 0 |
|
||
| 2 | `gap` | u16: a double box's gap; otherwise 0 |
|
||
| 1 | `aspect` | a double box's tile aspect, live-compose §3.1's `aspect`, 0 to 3; otherwise 0 |
|
||
|
||
A frame of kind 0 has every other field 0. A squeeze or a double box has a `width` and a
|
||
`height` live-compose §3.4 admits (each even, 16 to 1920, at most 2 073 600 pixels), and
|
||
fields live-compose §3.3 admits for its layout: a squeeze's `window_w` from 1 to `width` − 1 and
|
||
`window_h` from 1 to `height` − 1, with `gap` and `aspect` 0; a double box's `corner` 1 or 2,
|
||
`window_w` and `window_h` 0, and `gap` + 2 at most `width`.
|
||
|
||
### 4.4 Digest, signature and rules
|
||
|
||
The **digest** of a record is SHA-256 over its bytes from `magic` through `signer`. The
|
||
**signature** is Ed25519 by `signer` over `wai:stitched-pod-input` (ASCII) and 0x01, followed by
|
||
the bytes the digest covers.
|
||
|
||
A reader reads exactly the bytes it is given as one record, in the order of §4.1, and refuses
|
||
it `pod_input_malformed` when the bytes end before a field the layout requires or go on after
|
||
`sig`, or when:
|
||
|
||
1. its magic is not `WSI1` or its version not 1, or its `carriage` is not 1, 2 or 3;
|
||
2. `avail_id` is not a text field, or `avail_ms` is 0;
|
||
3. `response_format` is not 1, 2 or 3; or `id_kind` is not 1 or 2 with format 1 or 2, or not 3
|
||
with format 3;
|
||
4. `id_attr` is, for carriage 1, not 1 to 255 bytes of `A`–`Z`, `0`–`9` and `-`; for carriage
|
||
3, not an XML name of 1 to 255 bytes (an ASCII letter or `_`, then ASCII letters, digits,
|
||
`_`, `-` and `.`); for carriage 2, not empty;
|
||
5. `currency` is not three of `A`–`Z`, or `exponent` is above 4;
|
||
6. the frame breaks §4.3;
|
||
7. the measurement terms break ad-presentation §8, or do not set its bit 0, **claim**: an ad
|
||
of a record counts only through a claim;
|
||
8. `n` is 0 or above 16; an ad's `slot` is not its index; `ad_id` or `serving_id` is not a text
|
||
field; `creative_id` and `registry` are not both empty, or both text fields; `duration_ms`
|
||
is 0; `price_micros` is above 10^12; `price_source` is not 1, 2 or 3, is 1 with a response
|
||
format other than 1, is 2 with format 3, or is other than 3 with format 3; `evidence` is not
|
||
zero exactly when `price_source` is not 2;
|
||
9. two ads have one `marker`.
|
||
|
||
### 4.5 A form-1 receipt as the response
|
||
|
||
A stitcher that decides by `wai.video.ssai`'s own deterministic decision (SPEC §5) seals a
|
||
form-1 receipt, whose signed bytes are unchanged here. A record of format 3 names it by its
|
||
identity, and a verifier that holds it checks it (§7.2). Its **JSON carriage**, for a verifier
|
||
to be given it, is one JSON object with exactly these members, a byte field lowercase
|
||
hexadecimal of its exact length:
|
||
|
||
| member | value |
|
||
|---|---|
|
||
| `form` | 1 |
|
||
| `avail_id` | a string |
|
||
| `manifest_hash` | 32 bytes: the attested manifest's hash |
|
||
| `pod_hash` | 32 bytes |
|
||
| `impressions` | an array, each `{"slot", "ad_id", "creative_hash", "duration_ms", "price_micros", "quartiles_reached"}`, as live-ads §8.2 writes a form-2 impression |
|
||
| `merkle_root` | 32 bytes |
|
||
| `budget_spent_micros`, `work_decide_ops`, `joules_micro` | integers |
|
||
| `energy_provenance` | its class's label, or `null` unmetered; and the members a class with evidence carries, as live-ads §8.2 has them. The legacy label `HwShunt` does not read in this carriage |
|
||
| `grant` | `{"grant_hash", "capability", "joule_ceiling_micro", "funds_ceiling"}`, `funds_ceiling` `null` for none |
|
||
| `parent_receipt_hash` | 32 bytes, or `null` |
|
||
| `signer_pubkey` | 32 bytes |
|
||
| `signer_id` | a string |
|
||
| `sig` | 64 bytes |
|
||
|
||
Its **signing payload** is the concatenation of `wai:video-ssai-receipt` (ASCII) and 0x01; the
|
||
length (u32) and bytes of `avail_id`; `manifest_hash`; `pod_hash`; `merkle_root`;
|
||
`budget_spent_micros`, `work_decide_ops` and `joules_micro` (u64 each); the class in its signed
|
||
encoding (energy-measurement §2.7), the tag byte 0 for none; the grant reference as live-ads
|
||
§8.4 writes it; 1 and `parent_receipt_hash`, or 0; the impressions' count (u64) and each one's
|
||
leaf (live-ads §8.3); and `signer_pubkey`, then the length (u32) and bytes of `signer_id`. Its
|
||
integers are big-endian. `sig` is Ed25519 by `signer_pubkey` over the payload, and the
|
||
receipt's identity is BLAKE3 over `wai:video-ssai-receipt-id` (ASCII) and 0x01, the payload
|
||
and `sig`. A receipt **verifies** when its signature verifies; its `merkle_root` is the root of
|
||
its impressions (live-ads §8.3); its `budget_spent_micros` is their billable spend and, when
|
||
`funds_ceiling` is not `null`, at most it; `work_decide_ops` is at least the impressions'
|
||
count; its slots are 0, 1, 2, … in order with each `quartiles_reached` at most 5; and its
|
||
figure and class cohere as energy-measurement §2 has them.
|
||
|
||
### 4.6 The session and the shared break
|
||
|
||
A record of a session is a decision made for one playback session: a personalised stitched
|
||
playlist, or the asset list one viewer was served. Its `session` is the session the player's
|
||
session key names (§5.2). The signer **MUST** take it from the player's session key, which the
|
||
player gives with its request for the session's playlist or asset list, by a means this revision
|
||
does not fix (for one, a parameter of that request, which a stitcher forwards in the ad request
|
||
it makes for the session). A signer **MUST NOT** derive a session from anything else, and a
|
||
claim counts toward a record of a session only when its key names that session (§7.3).
|
||
|
||
A **shared** record is a decision every viewer of the stream is shown: a break of a linear
|
||
channel, or a composited break all viewers see in the same picture. Each viewer's player that
|
||
presents it signs a claim under its own session key, and each claim of a distinct session that
|
||
is presented is one presentation (§7.4). A shared break is where a party that makes session
|
||
keys of its own can sign claims for sessions nobody watched; a record that buys such breaks
|
||
**SHOULD** set ad-presentation §8's **attest** bit, so that each presentation costs that party
|
||
a vouched device (ad-presentation §13).
|
||
|
||
## 5. The stitched claim
|
||
|
||
### 5.1 Layout
|
||
|
||
| size | field | meaning |
|
||
|---|---|---|
|
||
| 4 | `magic` | `WSC1` (ASCII) |
|
||
| 1 | `version` | 1 in this revision |
|
||
| 1 | `carriage` | the marker's carriage (§3.1) |
|
||
| 32 | `manifest` | SHA-256 of the manifest the player read the marker from: the Media Playlist for carriage 1, the asset list for carriage 2, the MPD for carriage 3 |
|
||
| 16 | `session` | the session its key names (§5.2) |
|
||
| 32 | `creative_hash` | the creative presented: its creative digest (ad-presentation §4) |
|
||
| 32 | `player` | the session key, which signs the claim |
|
||
| 1 | `geometry` | ad-presentation §5.1's: 1 the ad's own pixels (in a frame, its region, §6), 2 a full-screen television output, 3 the player's area |
|
||
| 2 | `share_bp` | u16: the share of the picture the ad's region holds, in basis points (§6) |
|
||
| 4 | `frames` | u32: as ad-presentation §5.1 |
|
||
| long | `marker` | the marker's body (§3.1) |
|
||
| short | `ad_id` | the ad identity the marker carries: a text field |
|
||
| 2 | `n` | u16: the presented objects' count |
|
||
| 32 × `n` | `objects` | the content hashes of the creative's objects the spans touch, in order |
|
||
| 2 | `m` | u16: the spans' count |
|
||
| 11 × `m` | `spans` | ad-presentation §5.2's spans, over the ad's media time |
|
||
| long | `attestation` | a token (ad-presentation §7.1), or no bytes |
|
||
| 64 | `sig` | §5.3 |
|
||
|
||
### 5.2 The session key and the session
|
||
|
||
A player **MUST** make a fresh Ed25519 key for each playback session of a stream, from its
|
||
platform's random generator, and **MUST NOT** use it for another session or derive it from an
|
||
identifier of the viewer, the device or an account (ad-presentation §3). The **session** a key
|
||
names is the first 16 bytes of SHA-256 over `wai:stitched-presentation-session` (ASCII) and
|
||
0x01, and the key (32 bytes). It is a function of a key made for one session alone, so it links
|
||
nothing across sessions.
|
||
|
||
### 5.3 Digest and signature
|
||
|
||
The **digest** of a claim is SHA-256 over its bytes from `magic` through `attestation`. The
|
||
**signature** is Ed25519 by `player` over `wai:stitched-presentation-claim` (ASCII) and 0x01,
|
||
followed by the bytes the digest covers.
|
||
|
||
### 5.4 Reading and rules
|
||
|
||
A reader reads exactly the bytes it is given as one claim, in the order of §5.1, and refuses it
|
||
`stitched_claim_malformed` when the bytes end before a field the layout requires or go on after
|
||
`sig`, or when:
|
||
|
||
1. its magic is not `WSC1` or its version not 1;
|
||
2. `carriage` is not 1, 2 or 3, or `marker` is not a body of that carriage that reads (§3.1);
|
||
3. `geometry` is not 1, 2 or 3, or `share_bp` is not 1 to 10 000;
|
||
4. `ad_id` is not a text field;
|
||
5. its objects and spans break ad-presentation §5.4 rules 4 to 6.
|
||
|
||
### 5.5 The attestation challenge
|
||
|
||
A token a claim carries answers ad-presentation §7.2's TokenChallenge with the record's
|
||
`token_type` and `issuer` and this **redemption context**: SHA-256 over
|
||
`wai:stitched-presentation-attest` (ASCII) and 0x01, `carriage` (one byte), the marker digest,
|
||
`session`, `creative_hash`, `player`, and the length (one byte) and bytes of `ad_id`, the
|
||
claim's own. It is a function of the ad's place in the stream and of the session key, each
|
||
known before the ad plays, so a token answers one presentation of one session key; lifted onto
|
||
another claim, its `challenge_digest` is not that claim's. The token is verified as
|
||
ad-presentation §7.3 verifies one.
|
||
|
||
## 6. Composited frames
|
||
|
||
A break may show the ad and the program in one picture: a **squeeze**, the program shrunk into
|
||
a window in one corner with the ad in the L around it; or a **double box**, the program and the
|
||
ad in two boxes side by side. A record states such a frame (§4.3), and the ad's **region** is
|
||
then live-compose §5.9's ad region of a canvas of the frame's `width` and `height` under its
|
||
layout, `corner`, program window (`window_w` × `window_h` as `pip_width` × `pip_height`),
|
||
`gap` and `aspect`: an integer function of the signed fields, so a player and a verifier compute
|
||
the same rectangles. A frame of kind 0 is the full picture, and the region is the whole of it.
|
||
|
||
- The **share** of the picture the region holds is live-compose §5.9's, in basis points; 10 000
|
||
for the full picture. A claim states it as `share_bp`, and a claim whose `share_bp` is not its
|
||
record's frame's share is refused (§7.3 step 9).
|
||
- A player that measures the ad's own pixels (geometry 1) **MUST** measure the fraction in
|
||
view of the ad's **region**, not of the whole picture: the share of the region's area, as the
|
||
picture is displayed, that lies within the visible part of its player (ad-presentation §10).
|
||
One that cannot place the region states geometry 3, its own area, as ad-presentation §10
|
||
allows.
|
||
- The viewability rules of ad-presentation §6.1 then apply to the region's pixels: half of the
|
||
ad's region in view for the video rule, all of it for the cross-media rule. No text read for
|
||
this revision defines a viewability rule of its own for a composited ad; a buyer that weighs a
|
||
composited presentation by how much of the picture the ad held reads `share_bp` among the
|
||
claim's facts.
|
||
- **One presentation is one claim per signing player.** Every viewer of a composited break
|
||
sees the same picture, and each player that presents it signs its own claim; a record of the
|
||
break is shared (§4.6), and each presented claim of a distinct session is one presentation.
|
||
A count of devices that requested the stream is not a count of presentations.
|
||
|
||
## 7. Verification
|
||
|
||
### 7.1 Inputs
|
||
|
||
A verifier is given:
|
||
|
||
- **records**: pod-input records, a list of byte strings, in an order;
|
||
- **claims**: claims, a list of byte strings, in an order, at most 2^20 of them at once, so that
|
||
no spend of §7.4 passes 2^64 − 1 (a price is at most 10^12, and a claim is one presentation
|
||
of one ad at most);
|
||
- **manifests**: the manifests the players read (playlists, asset lists and MPDs), by their
|
||
bytes;
|
||
- **responses**: VAST and VMAP documents, by their bytes; and form-1 receipts in their JSON
|
||
carriage (§4.5);
|
||
- **creatives**: the creatives it holds (ad-presentation §4), each its objects;
|
||
- **issuers**: the attestation issuers it trusts (ad-presentation §7.3);
|
||
- **signers**: the keys whose records it accepts: the stitchers and proxies the buyer and the
|
||
seller agreed on.
|
||
|
||
### 7.2 Each record
|
||
|
||
The records are judged first, in order. Each gets one verdict, **accepted** or the reason of
|
||
the first step it fails:
|
||
|
||
1. It reads (§4.4): `pod_input_malformed`.
|
||
2. Its signature verifies under `signer` (§4.4): `bad_key`, `bad_signature`.
|
||
3. `signer` is among the signers: `pod_signer_unknown`.
|
||
4. No record before it that was accepted has its `session` and an ad of a `marker` it has:
|
||
`pod_duplicate`. A decision is recorded once.
|
||
5. **The response.** When the verifier holds it, the record agrees with it, as below:
|
||
otherwise `pod_response_mismatch`.
|
||
- Format 1, the response whose SHA-256 is `response`: it reads as XML (§3.5) with a root of
|
||
local name `VAST`; and for each ad, exactly one `Ad` child of the root has the ad's
|
||
`serving_id` as its identity (for `id_kind` 1, the text of the first `AdServingId` child
|
||
of its first `InLine` child; for 2, its `id` attribute); that `Ad` has an `InLine` child;
|
||
when `creative_id` is not empty, a `UniversalAdId` child of a `Creative` child of a
|
||
`Creatives` child of that `InLine` has the text `creative_id` and the `idRegistry`
|
||
`registry`; and when `price_source` is 1, that `InLine`'s first `Pricing` child has
|
||
`model` `CPM` and `currency` the record's `currency`, and its text is a decimal number
|
||
(digits, then optionally `.` and digits) *P* with *P* × 10^(`exponent` + 3) an integer of
|
||
at most 2^64 − 1 equal to `price_micros`.
|
||
- Format 3, the receipt whose identity is `response`, among those that read (§4.5): it
|
||
verifies (§4.5), and its impressions are the record's ads slot for slot: each one's
|
||
`slot` the ad's, its `ad_id` the ad's `serving_id`, and its `creative_hash`,
|
||
`duration_ms` and `price_micros` the ad's.
|
||
- Format 2 is bound by its digest and not read.
|
||
|
||
A record whose response the verifier does not hold is accepted with its response unchecked,
|
||
and the verification states so (§7.5).
|
||
|
||
A record that is not accepted names no ad: a claim of one of its ads is judged as though it
|
||
were not there.
|
||
|
||
### 7.3 Each claim
|
||
|
||
Then the claims, in order. Each gets one verdict, **presented** or the reason of the first
|
||
step it fails:
|
||
|
||
1. It reads (§5.4): `stitched_claim_malformed`.
|
||
2. Its signature verifies under `player` (§5.3): `bad_key`, `bad_signature`.
|
||
3. Its `player` names its `session` (§5.2): `claim_key_unbound`.
|
||
4. **Its record.** Its record is the first accepted record of its `carriage` that has an ad
|
||
whose `marker` is the claim's marker digest and whose `ad_id` is the claim's, and whose
|
||
`session` is the claim's or which is shared. When an accepted record has such an ad but none
|
||
is the claim's this way: `claim_session_unbound`. When none has such an ad:
|
||
`claim_no_decision`. From here on the claim names that record and that ad.
|
||
5. **Its manifest.** The verifier holds a manifest whose SHA-256 is `manifest`:
|
||
`claim_manifest_unknown`.
|
||
6. **Its marker.** The marker, with the claim's ad identity, is read from that manifest by §3
|
||
under the record's `id_attr`: for carriage 1, the manifest is a Media Playlist with a date
|
||
range tag of the marker's `ID` and `START-DATE` whose `id_attr` attribute's value is the
|
||
ad identity; for carriage 2, it is an asset list whose asset at the marker's index has the
|
||
ad identity as its `URI`, and a manifest the verifier holds is a Media Playlist with an
|
||
interstitial date range of the marker's `ID` and `START-DATE`; for carriage 3, it is an MPD
|
||
with an Event of the marker's fields whose `id_attr` attribute's value is the ad identity:
|
||
`claim_marker_absent`.
|
||
7. **One claim a session.** No claim before it passed steps 1 to 6 for the same record, ad and
|
||
session: `claim_duplicate`. The first that does is that session's **own claim** of the ad,
|
||
whatever its later verdict; steps 8 to 11 are for own claims alone.
|
||
8. **The asset.** Its `creative_hash` is the ad's; the verifier holds a creative of that
|
||
creative digest whose durations sum to the ad's `duration_ms`; every span ends at most at
|
||
that `duration_ms`; and `objects` is exactly the content hashes of the creative's objects
|
||
the spans touch, in order (ad-presentation §9.2 step 6): `claim_asset_mismatch`.
|
||
9. **The frame.** Its `share_bp` is the share of its record's frame (§6):
|
||
`claim_frame_mismatch`.
|
||
10. **Viewability.** It is viewable under the record's rule (ad-presentation §6.2, with
|
||
`geometry` and the **audible** bit as there): `claim_not_viewable`.
|
||
11. **Attestation**, when the record sets **attest**: its token is not empty
|
||
(`attestation_absent`); it reads as a token of the record's `token_type`, its
|
||
`challenge_digest` is SHA-256 of the TokenChallenge of §5.5, and its issuer's verifier
|
||
accepts it under the record's `issuer` (`attestation_invalid`).
|
||
|
||
### 7.4 Presentations, quartiles and the spend that counts
|
||
|
||
For each ad of each accepted record:
|
||
|
||
- its **presentations** are its own claims (one per session, step 7) that are presented. A
|
||
record of a session has at most one; a shared record has one for each session whose claim is
|
||
presented;
|
||
- the **quartile** a presentation reaches is ad-presentation §6.3's, over the claim's spans
|
||
with *D* the ad's `duration_ms`: 1 started, then a quarter, a half, three quarters and all of
|
||
it, at VAST 4.3 §3.14.1's marks. The ad's **quartiles** are five counts: its presentations
|
||
whose quartile is at least 1, 2, 3, 4 and 5;
|
||
- its **verdict** is presented when it has a presentation; otherwise `claim_absent` when it has
|
||
no own claim, or the verdict of its first own claim;
|
||
- its **presented spend** is its `price_micros` times its presentations.
|
||
|
||
A record's presented spend is the sum over its ads. The **spend** of a verification is, for
|
||
each currency, the sum of the presented spend of the accepted records of that currency. Spends
|
||
of different currencies are never summed.
|
||
|
||
A quartile is recomputed from what a player signed, not compared with a beacon count: an ad
|
||
whose segments were fetched ahead of its break (a prefetch) and never presented has no claim,
|
||
so no presentation and no spend, whatever a request-based count says. For a record of format 3
|
||
whose receipt was checked, the verification states the receipt's `quartiles_reached` for each
|
||
ad and its `budget_spent_micros` beside the presented ones, so a buyer and a seller see both
|
||
figures from the same signed bytes.
|
||
|
||
### 7.5 What a verification states
|
||
|
||
- For each record, in order: its digest when it reads, and its verdict; and for an accepted
|
||
record, whether its response was checked, each ad's slot, own claims (their indices among the
|
||
claims given), presentations, verdict, quartiles, presented spend and, for a checked format-3
|
||
response, its stated quartile; the record's presented spend, and its stated spend for a
|
||
checked format-3 response; and its frame's share (§6) and ad region, the rectangles of
|
||
live-compose §5.9, none for the full picture.
|
||
- For each claim, in order: its digest when it reads; its verdict; from step 4 on, the record
|
||
and the ad's slot it names; and for an own claim, from step 8 on, the quartile it reaches,
|
||
its run and its viewable time (ad-presentation §6.2).
|
||
- The spend, by currency.
|
||
|
||
A verification refuses nothing as a whole: each record and each claim gets its own verdict.
|
||
|
||
## 8. The producer, the tool and the service
|
||
|
||
**The reference producer** is `web/ads/stitched.js` of the browser client `joule-moq-web`,
|
||
beside ad-presentation's `web/ads/presentation.js`, whose span recorder it uses. From the
|
||
manifest a player fetched, it reads the markers and ad identities of §3 (`hlsAds`,
|
||
`interstitialAds`, `mpdAds`); it measures the fraction in view of an ad's region in a frame
|
||
(`regionVisibleBp`); and it writes and signs the claim with the platform's Ed25519. It needs no
|
||
player of a particular kind: a player integration hands it the manifest bytes, the ad's media
|
||
time and the observations ad-presentation §10 lists. Its tests require the corpus's claims from
|
||
the corpus's manifests, byte for byte.
|
||
|
||
**The tool**, `wai_stitched_presentation` (feature `stitched_presentation`): `verify` reads a
|
||
bundle (the inputs of §7.1 as JSON, in the corpus README's form) and prints what §7.5 states;
|
||
`sign-pod` writes and signs a record from a draft with the signer's key; `vectors` writes and
|
||
checks the corpus. **The service**, `wai_stitched_presentation serve`, answers `POST /v1/verify`
|
||
with the same statement for a bundle and `GET /v1/health`, over HTTP/1.1 on a loopback address
|
||
for a reverse proxy to publish; it reads one request a connection, bounds the head, the body
|
||
and the time it waits, and holds no state between requests.
|
||
|
||
## 9. Reasons
|
||
|
||
The codes this extension adds, each with where it is raised:
|
||
|
||
| reason | raised at |
|
||
|---|---|
|
||
| `pod_input_malformed` | §4.4, §7.2 step 1 |
|
||
| `pod_signer_unknown` | §7.2 step 3 |
|
||
| `pod_duplicate` | §7.2 step 4 |
|
||
| `pod_response_mismatch` | §7.2 step 5 |
|
||
| `stitched_claim_malformed` | §5.4, §7.3 step 1 |
|
||
| `claim_session_unbound` | §7.3 step 4 |
|
||
| `claim_manifest_unknown` | §7.3 step 5 |
|
||
| `claim_marker_absent` | §7.3 step 6 |
|
||
| `claim_frame_mismatch` | §7.3 step 9 |
|
||
|
||
A verification also gives live-events' `bad_key` and `bad_signature` (§7.2 step 2, §7.3
|
||
step 2), and ad-presentation's `claim_key_unbound` (§7.3 step 3), `claim_no_decision` (step 4),
|
||
`claim_duplicate` (step 7), `claim_asset_mismatch` (step 8), `claim_not_viewable` (step 10),
|
||
`attestation_absent` and `attestation_invalid` (step 11), and `claim_absent` (§7.4), each at the
|
||
step named. None of them refuses a verification: each is one record's, one claim's or one ad's.
|
||
|
||
## 10. Conformance
|
||
|
||
[`live-conformance/stitched/`](../live-conformance/stitched/) holds the corpus, written from
|
||
fixed seeds by `wai_stitched_presentation vectors gen`: `vectors.json`, which holds the
|
||
creatives, the attestation issuer (RFC 9578 Appendix A.2's first test key) and the signers the
|
||
verifier holds, the players' session keys, and each vector's records, claims and facts; and
|
||
each vector's documents as files of their own. Every document is a fixture written for its
|
||
case. The vectors:
|
||
|
||
- **hls_stitched**: two viewers' personalised playlists with ads stitched in, each ad under a
|
||
date range that carries its ad identity; a record per viewer priced from its VAST response;
|
||
viewer 2 left before its second ad, whose segments had been fetched ahead of the break, so
|
||
that ad has no presentation; a control of each check of a record (an unknown signer, a price
|
||
the response does not give, a decision recorded twice, terms that count without a claim, a
|
||
flipped signature) and of a claim (a duplicate, a key that does not name the session, a
|
||
session the record was not decided for, a marker no record names, a manifest not held, a
|
||
manifest without the marker, a truncated claim, a flipped signature);
|
||
- **hls_interstitial**: one shared playlist with an interstitial date range and an asset list
|
||
for each viewer; records under the cross-media rule with an audible run and attestation, one
|
||
ad priced by a winning bid whose document is bound by digest; attested presentations, a claim
|
||
with no token and one carrying another presentation's token;
|
||
- **squeezeback**: a squeeze on a DASH channel marked by an event; one shared record; three
|
||
presentations, among them one that stopped at the midpoint, and claims whose share is not the
|
||
frame's, whose region was too little in view, given twice, and of another object;
|
||
- **double_box**: a double box on an HLS channel, its record in another currency at a price the
|
||
signer states, its VMAP response bound and not read; a presentation measured over the ad's box
|
||
and one over the player's area;
|
||
- **form1_prefetch**: a stitcher's own decision sealed in a form-1 receipt whose beacons counted
|
||
the second ad as started when its first segment was fetched ahead of the break; the viewer
|
||
left after the first ad; and a record naming the same receipt at another price.
|
||
|
||
A conforming verifier gives every record and claim its verdict and facts, every ad its
|
||
presentations, quartiles and spend, and every vector its spend by currency, as the corpus
|
||
states. `wai_stitched_presentation vectors verify` and the test `tests/stitched_presentation.rs`
|
||
check that every file regenerates byte for byte, that `wai-rs` gives every vector its facts,
|
||
and that §9 lists exactly the codes the reference adds; the generator checks that the corpus
|
||
gives every reason of §9 and every other code of a claim or a record but `bad_key`.
|
||
`live-conformance/stitched_presentation_verify.py` is the second implementation, written from
|
||
this text: it shares no code with `wai-rs`; it verifies Ed25519 with WAI's strict rule
|
||
(`claim-conformance/_ed25519.py`), BLAKE3 with `claim-conformance/_blake3.py`, a form-1
|
||
receipt's leaves and Merkle root as the live-ads corpus's second implementation computes them,
|
||
the spans, the run and the quartile and RSASSA-PSS with the ad-presentation corpus's second
|
||
implementation, and an ad frame with the live-compose corpus's; it reads playlists, asset lists,
|
||
MPDs and VAST documents itself; and it mutates copies of the corpus and requires every copy to
|
||
fail. The reference producer's tests (§8) require the corpus's claims from its manifests.
|
||
|
||
## 11. Security and privacy considerations
|
||
|
||
- **What a claim proves.** As ad-presentation §13 states for its claim: that the holder of the
|
||
session key stated these facts of this ad, at this place of a manifest it holds the bytes of.
|
||
Without attestation it removes the third party that strips, redirects or forges beacons, and
|
||
the count of segment requests as a count of impressions; it does not remove a dishonest player
|
||
or a party that makes session keys of its own, which a shared record (§4.6) is open to.
|
||
- **What a record proves.** That its signer stated these inputs of one decision: the response's
|
||
digest, the ads, their creatives and prices. A record of format 1 checked against the
|
||
response shows the ads and the CPM prices are the response's; one of a winning bid binds the
|
||
bid's document without reading it; one of format 3 checked against its receipt shows the
|
||
stitcher's decision and its stated quartiles. The signer is the party a buyer trusts for the
|
||
decision, and §7.1's signers are who those parties are.
|
||
- **The manifest.** A claim names the manifest by its digest, and a verifier that holds the
|
||
bytes checks that the stream carried the marker with that ad identity. A personalised playlist
|
||
is a viewer's; its bytes reach whoever verifies the claim, and a collector **SHOULD** keep
|
||
them no longer than the reconciliation they serve.
|
||
- **Linking.** A session key is made for one playback session, and the session derived from it
|
||
alone, so claims link nothing across sessions. A record of a session carries that session; a
|
||
shared record carries none. A claim carries no identifier of the viewer, the device or an
|
||
account.
|
||
- **Withholding.** A party between the player and the verifier can withhold a claim; the ad
|
||
then has no presentation, which is the seller's lost spend, never the buyer's overpayment.
|
||
- **A composited frame.** The region is fixed by the record's frame; a player that states
|
||
another share is refused, and one that measures the whole picture where it should measure the
|
||
region overstates its fraction in view, which attestation does not bound: a buyer that needs
|
||
the measurement itself bounded needs the player's integrity, as ad-presentation §13 says of
|
||
every claim.
|