open-standards/wai/extensions/delivery-telemetry.md
Transaction Science 793ed1f2dc
All checks were successful
wai-app and Create / wai-app · does this change touch it (pull_request) Successful in 7s
wai-audit collector and player integration / does this change touch it (pull_request) Successful in 10s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 12s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (pull_request) Successful in 14s
WAI operation catalogue / does this change touch it (pull_request) Successful in 13s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 13s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 14s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 7s
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Successful in 6s
wai-fleet service / does this change touch it (pull_request) Successful in 14s
WAI neural sink gates / does this change touch it (pull_request) Successful in 13s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 12s
wai-quantum for JavaScript / does this change touch it (pull_request) Successful in 9s
wai-ledger service / does this change touch it (pull_request) Successful in 14s
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 14s
wai-key-proxy / does this change touch it (pull_request) Successful in 16s
WAI conformance / does this change touch it (pull_request) Successful in 31s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 32s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 6s
WAI energy receipts / does this change touch it (pull_request) Successful in 33s
wai-sink-android native sink library / does this change touch it (pull_request) Successful in 8s
wai-sink-swift native sink package / does this change touch it (pull_request) Successful in 7s
WAI Studio / does this change touch it (pull_request) Successful in 8s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 9s
wai-web wasm32 / does this change touch it (pull_request) Successful in 25s
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Successful in 5s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (pull_request) Successful in 55s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 1m11s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m58s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 1m22s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 1m51s
wai-audit collector and player integration / collector · linux (pull_request) Successful in 1m32s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (pull_request) Successful in 2m31s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m51s
WAI neural sink gates / the exact JPEG-AI decoder reports its path (pinned inputs, required) (pull_request) Successful in 2m2s
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (pull_request) Successful in 3m36s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 4m32s
wai-fleet service / service · linux (pull_request) Successful in 2m42s
wai-key-proxy / proxy · linux (pull_request) Successful in 57s
wai-pack / wai-pack · linux (pull_request) Successful in 3m19s
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m30s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 27s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 5m59s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 4m11s
WAI operation catalogue / the catalogue and every door · linux (pull_request) Successful in 5m56s
wai-ledger service / service · linux (pull_request) Successful in 1m50s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 7m18s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 1m7s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m48s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (pull_request) Successful in 7m43s
wai-quantum for JavaScript / javascript · linux (pull_request) Successful in 3m41s
wai-fleet service / service · macos (pull_request) Successful in 48s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 3m39s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 4m54s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 3m2s
wai-pack / wai-pack · macos (pull_request) Successful in 3m27s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 5m55s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 13m52s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 5m28s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 16m34s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 11m38s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 27s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m7s
wai-ledger service / service · macos (pull_request) Successful in 1m6s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 9m35s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 10m56s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 17m20s
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Successful in 18m48s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 24m50s
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Successful in 18m38s
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Successful in 2m31s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 7m3s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m46s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 8m4s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m47s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 17m16s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 7m47s
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Successful in 34m23s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 21s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 16s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 7m5s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 18s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 37m20s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 10m52s
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Successful in 18m17s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 11m18s
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Successful in 39m59s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 15s
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Successful in 40m12s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 50s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m42s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 3m47s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 17m30s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 18m34s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 14m28s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 14m15s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 30m4s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h18m5s
wai/one-way-delivery, converted to the layout of the fix: no shared append point
The change of wai/one-way-delivery (f1258784d..987625394, 28 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.

- 25240c4f0 wai: timeliness — when each object of a group is of use, and whether each holder held it in time
- c84992362 wai: timeliness_verify.py — the timeliness corpus's second implementation
- 44036fcdc wai: timeliness in the registry, SPEC §5, the changelog and CI; http_receipts.py checks its claims
- 27ce6754c wai: claim_verify.py lists not-held-by-deadline, and the claim corpus holds a session that switches for it
- 73d8d8eb9 wai: timeliness corpus — each holder's own line in key order, a holder's earliest fallback, and §7.2–§7.3's unreached plan rules
- 64c735f0c wai: the files timeliness changes are clippy-clean — a let chain, and the fetch callback's type allowed
- b72dc36bf wai: rung price — what delivering a rung costs, by bearer and direction, and which option a comparer takes
- 4e905e213 wai: rung_price_verify.py — the rung price corpus's second implementation
- 711fa6126 wai: rung price in the registry, SPEC §5, the changelog and CI
- 8ab70654e wai: rung price corpus — a refusal for every form rule 1 and §6.1 set, and the neighbour order a plan gives
- 1bc465959 wai: rung price ranks energy by checked evidence — a class counts only on the seller's own per-hop records, current and under a confirmed key
- 9e97997d9 wai: rung_price — the evidence text bound at its exact sizes, and a key confirmed for another class confirming nothing
- 8f1168683 wai: rung price corpus — a key confirmed for another class, the uncertainty product, each edge and order of the evidence verdict, and the evidence a plan read
- 7330e26ff wai: rung price — §6.1 links jwp-receipts at its first mention, and §10 says a record's age is its room's word
- 12c099457 wai: cache authority — who served what a node holds, and on whose authority
- f8f5d3e09 wai: cache_authority_verify.py — the cache authority corpus's second implementation
- 354146619 wai: cache authority in the registry, SPEC §5, the changelog and CI; its corpus holds what §12 states
- 55c7f3031 wai: timeliness §6 reads holdings records alone; cache-authority §9 derives the lines that read every kind
- b8fc6f02b wai: cache authority's lines read records as timeliness does, its deadline claim meets the envelopes held, and its corpus fixes each order and instant
- 91c5291cf wai: the files cache authority changes are clippy-clean — a boxed envelope, the base's names named once, slices borrowed
- 8e479daff wai: a held envelope boxes both its forms, so neither outweighs the other
- 3ec8da807 wai: impression aggregates — ad presentations counted over a bearer with no return path
- 0433a35c3 wai: impression_aggregate_verify.py — the impression aggregates corpus's second implementation
- 7d8e150e4 wai: impression aggregates in the registry, SPEC §5, the changelog and CI; its corpus states each receipt whole and a control for every malformed file
- d5e8ace02 wai: impression aggregates' size bounds are checked at their edge, and §4.3's decides no outcome
- 4bc8e13bd wai: impression aggregates' table totals are exact, and a receipt given twice is checked when either occurrence was
- 3c9f20524 wai: key_log's two result enums keep their public form, and say so to clippy
- 987625394 wai: Item::with_authority and Count::instance_received build only with the feature that calls each

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-05 01:45:32 -05:00

378 lines
24 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WAI Extension: Delivery Telemetry
> Status: Draft. An **unsigned, advisory** observation about WAI delivery that a sink,
> edge or origin emits; its mapping onto the keys of CMCD version 2 (CTA-5004-B) and
> CMSD (CTA-5006), written as RFC 9651 Structured Field Values, with each key's class and
> what it reveals (§3.4); and the session metrics a log of observations gives, which
> [telemetry-aggregation](telemetry-aggregation.md) aggregates without the service holding
> any session's. Reference impl: `wai-rs` —
> `telemetry::{sf, observation, cmcd, cmsd, qoe}`; the `wai_telemetry` tool; the corpus
> [`telemetry-conformance/`](../telemetry-conformance/), with a differential against an
> independent open-source CMCD and CMSD library. Keywords **MUST**, **MUST NOT**,
> **SHOULD**, **MAY** are RFC 2119/8174.
## 1. Status of an observation
§1.1 No signature covers an observation. A relying party **MUST NOT** treat one as
evidence of delivery, presentation, quality or energy. What a session did is attested,
where it is, by receipts and claims ([jwp-receipts](jwp-receipts.md)), never by
telemetry.
§1.2 An observation **MUST NOT** carry a joule figure, an acquisition class or a quality
figure. It **MAY** carry content hashes and link hashes as pointers.
§1.3 Telemetry binds no receipt to CMCD or CMSD, and no receipt to telemetry.
## 2. The observation
§2.1 An observation is a JSON object with exactly these members; a member that does not
apply is `null`. Integers are unsigned and at most 2⁵³ − 1, and a member a report
carries as an Integer (§3.3: `sn`, `ts_ms`, the object's `d_ms`, `dl_ms`, `ttfb_ms`,
`ttlb_ms` and `rc`, and the metrics) at most 10¹⁵ − 1, the largest RFC 9651 Integer, so
every observation can be reported. A string a report carries is printable ASCII.
| member | type | meaning |
|---|---|---|
| `v` | integer | `1` |
| `sid` | string | 32 lowercase hexadecimal digits, fresh per session, derived from no identifier (§8) |
| `sn` | integer | the observation's sequence number in the session |
| `ts_ms` | integer or `null` | Unix time of the event in milliseconds; required for every event reported in Event mode (§3.1) |
| `event` | string | `request`, `response`, `start`, `present`, `switch`, `layer-change`, `stall-start`, `stall-end`, `frame-drop`, `capable`, `abandon` or `end` |
| `cid` | string or `null` | the entry object's content hash (64 lowercase hexadecimal digits), or a deployer's content id of 1–64 letters, digits, `.`, `_` and `-` |
| `st` | string | stream type: `v` (on demand), `l` (live) or `ll` (low-latency live) |
| `sf` | string | `o` for WAI objects over the HTTP binding; `h` or `d` for a WAI rendition referenced from HLS or DASH |
| `object` | object or `null` | for `request` and `response`: the object (§2.2) |
| `present` | object or `null` | for `present`: `{ unit, layers, first, late_ms, decode_us }`; `first` is `true` only for the session's first presentation; `layers` is at least 1 |
| `switch` | object or `null` | for `switch`: `{ unit, from, to, reason }`; `from` and `to` are `primary` or `fallback` and differ; `reason` is a §5 term |
| `layer_change` | object or `null` | for `layer-change`: `{ from, to }`, a change of `layer_cap`; `null` is no cap |
| `capable` | object or `null` | for `capable`: `{ outcome, reason, classical_floor }` (§2.3) |
| `cohort` | string or `null` | `control` or `wai`: the arm the content's service assigned the session to |
| `metrics` | object or `null` | `{ msd_ms, bsa, bsda_ms, dfa }`: media start delay, buffer starvations and their total duration since the session began, and dropped frames since the session began |
| `claim` | 64 hexadecimal digits or `null` | on `end` only: the link hash of the session's `reconstruct-session` claim ([jwp-receipts](jwp-receipts.md) §2.4.12), where the sink sealed one |
§2.2 **The object** has exactly the members `ot`, `url`, `content_hash`, `capability`,
`rendition`, `layer`, `layers_total`, `layer_cap`, `counted`, `transferred`, `d_ms`,
`dl_ms`, `ttfb_ms`, `ttlb_ms`, `rc`, `path` and `receipt`, each `null` where it does
not apply:
- `ot` is a CTA-5004-B object type token (`m`, `a`, `v`, `av`, `i`, `c`, `tt`, `k`,
`o`); `content_hash` the object's content hash and `receipt` a receipt's link hash,
each 64 lowercase hexadecimal digits; `capability` at most 64 characters;
`rendition` the `WAI2` rendition index; `layer` and `layers_total` the object's
stage and its stage set's stage count; `layer_cap` the cap the request asks for;
`counted` its counted bytes and `transferred` its transferred bytes
([staged-measurement](staged-measurement.md) §3); `path` `primary` or `fallback`;
`url` the object's address, at most 2048 characters, holding no `?`, `#`, `@` or `\`
(§8.2).
- The request keys `ot`, `d_ms`, `dl_ms` and `layer_cap` are `null` on a `response`;
the response keys `rc`, `ttfb_ms`, `ttlb_ms` and `url` are `null` on a `request`.
§2.3 **A `capable` observation** reports one WAI-arm dispatch outcome: `not-reached`
(no dispatch decision for a WAI object was reached), `missing-capability`,
`pin-refused`, `module-refused`, `below-rate` or `capable`. `reason` is SPEC §3.1's pin
refusal for `pin-refused` (one of the terms §5 lists before `missing-capability`), and
`null` otherwise. `classical_floor` is the platform's answer for the classical
rendition's codec string, `supported` or `unsupported`, where the sink asked. The
outcome comes from WAI's own dispatch: advertised capabilities, pin verification and a
timed decode. A sink **MUST NOT** derive it from a platform media-capability query,
which answers only for the platform's own decoders; it reports that answer separately,
as `classical_floor`. How sessions are assigned to arms, and the shares computed over
outcomes, are not specified in this revision.
§2.4 An observation that breaks §2.1–§2.3 is refused as a whole: a member missing or
not listed, a value of another type or outside its range, an object on an event that
has none, or none on one that has one.
## 3. CMCD version 2 mapping
§3.1 The modes:
- a `request` observation is reported in Request mode;
- every other observation is reported in Event mode, with `ts`: a `response` as an
event report with `e=rr`, whose response keys (`rc`, `ttfb`, `ttlb`, `url`) are never
carried in Request mode or in a header shard;
- a `present` observation with `first: false` is not reported in CMCD.
Every report carries `v=2`, `sid`, `sn`, `st` and `sf`, and `cid` where it is not `null`.
| observation | CMCD | mode |
|---|---|---|
| `sid`, `sn`, `cid`, `st`, `sf` | same keys | every report |
| `object.ot`, `object.d_ms`, `object.dl_ms` | `ot`, `d`, `dl` | request |
| `object.rc`, `object.ttfb_ms`, `object.ttlb_ms`, `object.url` | `rc`, `ttfb`, `ttlb`, `url`, with `e=rr` | event |
| `ts_ms` | `ts` | event |
| `start` / `end` / `abandon` | `e=ps`, `sta=s` / `e` / `q` | event |
| `present` with `first: true` | `e=ps`, `sta=p` | event |
| `stall-start` / `stall-end` | `e=ps`, `sta=r` / `p` | event |
| `metrics.msd_ms`, `metrics.dfa` | `msd`, `dfa` | any report |
| `metrics.bsa`, `metrics.bsda_ms` | `bsa`, `bsda`, each an inner list of one Integer | any report |
| `switch`, `capable`, `layer-change`, `frame-drop` | `e=ce`, `cen` = `"wai-switch"`, `"wai-capable"`, `"wai-layer-change"`, `"wai-frame-drop"` | event |
§3.2 An emitter **MUST NOT** put counted bytes, a layer or a capability into `br`, `tb`,
`pb` or any other bitrate key.
§3.3 A report is an RFC 9651 Dictionary: Integers for counts and times, Tokens for
`st`, `sf`, `ot`, `e` and `sta`, Strings for `sid`, `cid`, `url` and `cen`. Its keys are
written in bytewise order and without optional whitespace, and a key is written only
when its value is not `null`. The corpus fixes the output byte for byte.
§3.4 **Key classes.** Each key this mapping writes has a class:
- `delivery`: what the host serving the request needs to serve it;
- `measurement`: what is only aggregated afterwards.
A class informs what a deployment does with a key (§4.3,
[telemetry-aggregation](telemetry-aggregation.md) §9); it is not a permission, and no key
is conditional on anything but the observation's members. A Request-mode report goes to the
host that serves the object, an Event-mode report to the content's own service (§8.3).
| key | class | what it reveals | reported in |
|---|---|---|---|
| `v` | delivery | the CMCD version the report is written in | every report |
| `st` | delivery | whether the session plays on demand, live or low-latency live, which a host may serve differently | every report |
| `ot` | delivery | the kind of object requested, which a host may schedule or cache by | Request mode |
| `d` | delivery | the requested object's playback duration, which a host may prefetch by | Request mode |
| `dl` | delivery | the time until the object is needed: the sink's buffer level, or, for an object no buffer governs, its deadline ([timeliness](timeliness.md) §8.2) | Request mode |
| `science.transaction.wai-ch` | delivery | the exact object, by content hash, which a content-addressed host serves by | Request mode, `e=rr` |
| `science.transaction.wai-lc` | delivery | the layer cap the request asks the host to apply: the sink's quality ceiling | Request mode, `wai-layer-change` |
| `sid` | measurement | that reports belong to one session; it is fresh per session (§8.1), so nothing across sessions | every report |
| `sn` | measurement | the report's place in its session, so how many came before it | every report |
| `cid` | measurement | the content the session plays | every report |
| `sf` | measurement | the delivery format: WAI objects, or a WAI rendition through HLS or DASH | every report |
| `msd` | measurement | the session's media start delay | any report |
| `bsa`, `bsda` | measurement | the session's buffer starvations and their duration so far: its network conditions | any report |
| `dfa` | measurement | the frames dropped so far: the device's decode performance | any report |
| `ts` | measurement | when the event happened | Event mode |
| `e`, `sta` | measurement | the event and the player's state | Event mode |
| `rc`, `ttfb`, `ttlb` | measurement | a response's status and timing: the sink's throughput and network latency | Event mode |
| `url` | measurement | the address of a response's object | Event mode |
| `cen` | measurement | which of this mapping's custom events occurred | Event mode |
| `science.transaction.wai-b`, `science.transaction.wai-tx` | measurement | the bytes counted and transferred for an object | Request mode, `e=rr` |
| `science.transaction.wai-cap` | measurement | a capability the sink decodes: device-identifying | Request mode, `e=rr` |
| `science.transaction.wai-ri` | measurement | the rendition chosen: the sink's bandwidth and device class | Request mode, `e=rr` |
| `science.transaction.wai-l`, `science.transaction.wai-lt` | measurement | how far into a stage set the sink fetches | Request mode, `e=rr` |
| `science.transaction.wai-p` | measurement | whether the sink fell back to the classical rendition | Request mode, `e=rr`, `wai-switch` |
| `science.transaction.wai-rcpt` | measurement | a receipt the sink holds, which links the request to its receipt chain | Request mode, `e=rr` |
| `science.transaction.wai-lcf` | measurement | the previous layer cap | `wai-layer-change` |
| `science.transaction.wai-coh` | measurement | the experiment arm of the session | every report |
| `science.transaction.wai-u` | measurement | a unit presented or switched | `sta=p`, `wai-switch` |
| `science.transaction.wai-pl`, `science.transaction.wai-late`, `science.transaction.wai-dec` | measurement | the layers presented, lateness and decode time: device performance, device-identifying | `sta=p` |
| `science.transaction.wai-sw` | measurement | why the sink switched | `wai-switch` |
| `science.transaction.wai-co`, `science.transaction.wai-cr`, `science.transaction.wai-cf` | measurement | the dispatch outcome and the platform's classical-floor answer: device-identifying | `wai-capable` |
| `science.transaction.wai-clm` | measurement | the session's sealed claim, which links the session to its signed claim | `sta=e` |
A key this mapping does not write has no class here.
## 4. Custom keys
§4.1 Custom keys have the form `science.transaction.wai-<x>`: a lowercase first letter,
then lowercase letters, digits, `.` and `-`, with an inner hyphen (CTA-5004-B's
custom-key rule). Every custom key's value is a String, the type CTA-5004-B admits for
custom keys besides a Token; an integer member is written as its decimal digits.
| key | from | report | header shard (Request mode) |
|---|---|---|---|
| `science.transaction.wai-b` | `object.counted` | request, `e=rr` | `CMCD-Object` |
| `science.transaction.wai-tx` | `object.transferred` | request, `e=rr` | `CMCD-Object` |
| `science.transaction.wai-ch` | `object.content_hash` | request, `e=rr` | `CMCD-Object` |
| `science.transaction.wai-cap` | `object.capability` | request, `e=rr` | `CMCD-Object` |
| `science.transaction.wai-ri` | `object.rendition` | request, `e=rr` | `CMCD-Object` |
| `science.transaction.wai-l` | `object.layer` | request, `e=rr` | `CMCD-Object` |
| `science.transaction.wai-lt` | `object.layers_total` | request, `e=rr` | `CMCD-Object` |
| `science.transaction.wai-p` | `object.path`, or `switch.to`: `"p"` or `"f"` | request, `e=rr`, `wai-switch` | `CMCD-Object` |
| `science.transaction.wai-rcpt` | `object.receipt` | request, `e=rr` | `CMCD-Object` |
| `science.transaction.wai-lc` | `object.layer_cap`, or `layer_change.to` | request, `wai-layer-change` | `CMCD-Request` |
| `science.transaction.wai-lcf` | `layer_change.from` | `wai-layer-change` | — |
| `science.transaction.wai-coh` | `cohort` | every report | `CMCD-Session` |
| `science.transaction.wai-u` | `present.unit`, or `switch.unit` | `sta=p` (first presentation), `wai-switch` | — |
| `science.transaction.wai-pl`, `-late`, `-dec` | `present.layers`, `present.late_ms`, `present.decode_us` | `sta=p` (first presentation) | — |
| `science.transaction.wai-sw` | `switch.reason` | `wai-switch` | — |
| `science.transaction.wai-co`, `-cr`, `-cf` | `capable.outcome`, `capable.reason`, `capable.classical_floor` | `wai-capable` | — |
| `science.transaction.wai-clm` | `claim` | `sta=e` | — |
§4.2 In Request mode the report also goes in header shards: each key to the shard
CTA-5004-B assigns it (`v`, `sid`, `cid`, `st`, `sf` and `msd` to `CMCD-Session`; `sn`,
`dl` and `dfa` to `CMCD-Request`; `ot` and `d` to `CMCD-Object`; `bsa` and `bsda` to
`CMCD-Status`), and each custom key to the shard of §4.1. Each shard is a Dictionary
written as §3.3 writes a report; a shard with no key is not sent.
§4.3 **Delivery-only Request mode.** A sink **MAY** report Request mode delivery-only: a
Request-mode report then carries the members of its observation whose keys are of the
delivery class (§3.4) and no other, written as §3.3 writes a report, in the header shards of
§4.2 when it goes in shards. The measurement-class members are not sent to the host; the
sink reports them to the content's own service in Event mode, or as the measures of
[telemetry-aggregation](telemetry-aggregation.md) §3. A reader that checks a report it is
told is delivery-only refuses one that carries a key not of the delivery class,
`measurement_key_in_delivery`, naming the first such key in bytewise order.
## 5. Switch reasons
§5.1 A switch reason is one of:
- SPEC §3.1's pin refusals, in the order of its checks: `pin-malformed`,
`form-not-registered`, `form-unreadable`, `form-required`, `prior-revoked`,
`prior-not-authorised` (check 5, at a sink that holds a key log), `prior-not-held`,
`digest-superseded`, `set-malformed`, `part-mismatch`, `set-incomplete`;
- `missing-capability`;
- `module-unverified`, `module-revoked`, `trap`, `fuel-exhausted`, `memory-exhausted`,
`deadline-threshold`, `unit-hash-mismatch`, `budget-exhausted`;
- `not-held-by-deadline`: the primary was not held by its deadline and its fallback was
presented ([timeliness](timeliness.md) §5.3, §8.6);
- `recovered`.
A later extension adds its reasons here.
## 6. CMSD mapping
§6.1 An origin or edge **MAY** describe a WAI object in `CMSD-Static` with `at`, `d`,
`ht`, `nor`, `nrr`, `ot`, `sf`, `st` and `su`, and the custom keys
`science.transaction.wai-ch` (its content hash), `-l` and `-lt` (its layer and its
stage set's stage count) and `-rcpt` (a delivery receipt's link hash), each a String.
`v` is not written for version 1.
§6.2 A deriving node adds `science.transaction.wai-op`, its operation token, as a Token.
§6.3 `CMSD-Dynamic` **MAY** state, for each node, `etp`, `rtt`, `mb`, `rd` and `du`, and
`science.transaction.wai-drop` (a String): the number of objects the node discarded on
delivery timeout since its previous response to the same session. Each node adds its
own entry, in order; within an entry the parameters are in bytewise order.
§6.4 Keys and parameters are written as §3.3 writes a report: in bytewise order, a key
only when its value is not `null`, and a Boolean (`su`, `du`) only when true.
§6.5 CMSD carries no joule figure. An edge's energy travels in a claim.
§6.6 **Reading.** A reader reads `CMSD-Static` as an RFC 9651 Dictionary and `CMSD-Dynamic`
as a List of Strings with parameters, in the subset §3.3 writes (Integers, Strings, Tokens and
Booleans; a dictionary member with parameters, or a Decimal, is outside it); a field it cannot
read so is `cmsd_malformed`. It passes over keys and parameters this section does not name: a
sender may add its own. A key it names with another type is `cmsd_type`: `at`, `d`, `ht`,
`etp`, `mb`, `rd` and `rtt` are non-negative Integers; `nor`, `nrr` and each node's server are
non-empty Strings; `ot`, `sf`, `st` and `science.transaction.wai-op` are Tokens; `su` and
`du` are Booleans, either value; `science.transaction.wai-ch` and `-rcpt` are Strings of 64
lowercase hexadecimal digits; `science.transaction.wai-l`, `-lt` and `-drop` are Strings of
decimal digits, with no leading zero but in `"0"` and at most 15 digits. A `v` other than the
Integer 1 is `cmsd_version` (a `v` that is not an Integer is `cmsd_type`). A field this section
writes reads back to the description it was written from.
| code | meaning |
|---|---|
| `cmsd_malformed` | the field is not a Dictionary or List of §3.3's subset |
| `cmsd_type` | a key or parameter §6.1 to §6.3 name holds another type or form |
| `cmsd_version` | the field states a CMSD version other than 1 |
## 7. Session metrics
§7.1 A **log** is one session's observations in `sn` order: one `sid`, `sn` strictly
increasing, timestamps never decreasing, and nothing after `end` or `abandon`.
§7.2 A log gives, in integer milliseconds and bytes:
- `started`: the log has a `start`;
- `vst_ms`, the video start time: the first presentation's `ts_ms` less the `start`'s,
`null` without both;
- `exit_before_start`: an `abandon` before any first presentation;
- `start_failed`: a session that started, never presented, and ended with `end`;
- `stalls` and `stall_ms`: the stalls begun after the first presentation, each from its
`stall-start` to its `stall-end`, or to `end` or `abandon` where it is still open,
and their total duration;
- `engaged_ms`: from the first presentation to `end` or `abandon`;
- `units`: the distinct units presented;
- `counted`: the counted bytes of every `response`;
- `claim`: the `end` observation's `claim`.
§7.3 These are statements of the emitter, as the observations are (§1.1).
## 8. Privacy
§8.1 `sid` is fresh per session, and an emitter adds no persistent identifier. Telemetry
goes to the host that serves an object (Request mode) and to the content's own service
(Event mode), and, for a measure of [telemetry-aggregation](telemetry-aggregation.md) §3, to
its task's two aggregators as secret shares, each of which alone reveals nothing about the
measurement. This extension sets no consent requirement for the service's own mechanics:
using a distributor's WAI service is the person's agreement to how that service works. Where
a regime asks for a lawful basis for processing telemetry, the service records it in a
`lawful-basis` claim ([jwp-receipts](jwp-receipts.md) §2.4.6). What each key reveals, and to
whom, is §3.4's.
§8.2 Neither a URL's query nor a signed URL's token enters an observation. A reader
refuses an `object.url` that holds a `?`, a `#`, a `@` or a `\`: a query, a fragment,
user information, or a separator some URL parsers read as `/` and so a way to user
information past a check for it. It refuses rather than strips them, and a path that
holds a `@` is refused with the rest. An emitter whose URLs carry a token in their
path **MUST** report `url` as `null`. `cid`'s syntax (§2.1) admits a content hash or
a short identifier, and no syntax keeps an address, a token or personal data out of an
identifier: an emitter **MUST NOT** put an address, a query, a token or personal data
in `cid`, and reports the content hash where it has no identifier free of them.
§8.3 A Request-mode report travels with its request, to the host that serves the object
and to no other party. Event-mode reports go to the content's own service (§8.1).
§8.4 A published aggregate covers at least 50 sessions per cell. A cell with fewer is
suppressed, not rounded. Where the figures are aggregated under
[telemetry-aggregation](telemetry-aggregation.md), the service never holds a session's
figures, and the floor is each aggregator's: a task's minimum batch size is at least 50, and
an aggregator refuses to release a share of a smaller batch (§4.3, §5.2 there). This
suppression still applies to what that extension does not aggregate (§9.1 there).
## 9. Conformance
§9.1 [`telemetry-conformance/`](../telemetry-conformance/) holds:
- observations of every event kind, with their expected CMCD reports and header shards
(`expected/cmcd.json`);
- observations every reader refuses (`invalid/`);
- CMSD-Static and CMSD-Dynamic descriptions with their expected fields
(`expected/cmsd.json`);
- session logs with their expected metrics (`expected/qoe.json`);
- CMSD fields to read (`cmsd-read/cases.json`), as §6.1 to §6.3 write them, in another key
order, with `v=1` and with keys of other senders, and fields each code of §6.6 refuses, with
their outcomes (`expected/cmsd-read.json`).
§9.2 `wai_telemetry verify` requires every expected value byte for byte, and every
`invalid/` observation refused.
§9.3 `cml/differential.mjs` builds each report from this extension's tables, encodes it
with an independent open-source CMCD and CMSD library at the versions
`cml/package-lock.json` locks, and requires the expected bytes; reads the
expected strings back as a dictionary, as a query string and as header shards, and
requires the report's members; and validates every report, and the Event-mode reports
together, with no error.
§9.4 The delivery-only reports of §4.3, with reports a reader refuses, and the key classes
of §3.4, are checked in the corpus of
[telemetry-aggregation](telemetry-aggregation.md) §12.
## 10. Wire names
§10.1 The custom keys this extension puts in CMCD (CTA-5004-B) and CMSD (CTA-5006) reports,
each in full:
| key | report | section |
|---|---|---|
| `science.transaction.wai-b` | CMCD | §4.1 |
| `science.transaction.wai-tx` | CMCD | §4.1 |
| `science.transaction.wai-ch` | CMCD, CMSD-Static | §4.1, §6.1 |
| `science.transaction.wai-cap` | CMCD | §4.1 |
| `science.transaction.wai-ri` | CMCD | §4.1 |
| `science.transaction.wai-l` | CMCD, CMSD-Static | §4.1, §6.1 |
| `science.transaction.wai-lt` | CMCD, CMSD-Static | §4.1, §6.1 |
| `science.transaction.wai-p` | CMCD | §4.1 |
| `science.transaction.wai-rcpt` | CMCD, CMSD-Static | §4.1, §6.1 |
| `science.transaction.wai-lc` | CMCD | §4.1 |
| `science.transaction.wai-lcf` | CMCD | §4.1 |
| `science.transaction.wai-coh` | CMCD | §4.1 |
| `science.transaction.wai-u` | CMCD | §4.1 |
| `science.transaction.wai-pl` | CMCD | §4.1 |
| `science.transaction.wai-late` | CMCD | §4.1 |
| `science.transaction.wai-dec` | CMCD | §4.1 |
| `science.transaction.wai-sw` | CMCD | §4.1 |
| `science.transaction.wai-co` | CMCD | §4.1 |
| `science.transaction.wai-cr` | CMCD | §4.1 |
| `science.transaction.wai-cf` | CMCD | §4.1 |
| `science.transaction.wai-clm` | CMCD | §4.1 |
| `science.transaction.wai-op` | CMSD-Static | §6.2 |
| `science.transaction.wai-drop` | CMSD-Dynamic | §6.3 |
§10.2 The registry lists each as a wire identifier of kind `telemetry-key`
([REGISTRY](../REGISTRY.md)).