extensions/telemetry-aggregation.md: four measures over session metrics (Prio3SumVec, Prio3Sum, Prio3Histogram), the DAP task's task_info layout and id, the floor of fifty reports each aggregator enforces, the WQS1 statement each aggregator signs, the WQA1 receipt the collector signs, verification, and a room's cell. delivery-telemetry: a class and what it reveals for every CMCD key the mapping writes (§3.4), delivery-only Request mode (§4.3), §8.1 stated as facts with no consent gate, §8.4 pointing to aggregation. live-metering §4.7/§6.5: a room may aggregate its QoE instead of sending logs. wai-rs: HPKE base mode (RFC 9180 A.1.1), DAP-20 messages, a reference client, leader, helper and collector, the receipt verifier, and the corpus generator; live-conformance/aggregate/vectors.json from fixed seeds. Co-Authored-By: Claude Opus 5.5 <[email protected]>
320 lines
21 KiB
Markdown
320 lines
21 KiB
Markdown
# 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 sink's buffer runs out: its buffer level | 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`;
|
||
- `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.
|
||
|
||
## 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`).
|
||
|
||
§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.
|