The libraries of 76383460e taken up by the relay (src/monitoring.rs), on probes::Served, each
part off unless the operator turns it on ([monitoring] in relay.example.toml). The relay's
ResourceID is derived from the public half of its summary key, never a network address.
- Metrics track (draft-jennings-moq-metrics-03): under (metrics.moq.arpa, v1, id) a catalog
and data track 6, one group a capture of control::exposition, the text GET /metrics
answers at that instant.
- Log track (draft-jennings-moq-log-04): a track a severity; session_opened, session_closed
and feedback_refused, and Relay::monitor().log for an embedder. A message with a network
address in an attribute is refused (address_in_attribute) and never published.
- Both served to a subscription a grant covers, from the next group, one queued at a time;
one that falls behind is ended TOO_FAR_BEHIND. PUBLISH into either namespace is
UNAUTHORIZED, FETCH NOT_SUPPORTED.
- qlog (draft-pardue-moq-qlog-moq-events-08), behind the new feature qlog: one .sqlog a
session, lengths and never bytes; stream_id is the relay's count of the session's streams.
Requests the relay opens upstream (FETCH, PUBLISH_NAMESPACE onward, SUBSCRIBE_NAMESPACE
onward) are logged too.
- Feedback (draft-liu-moq-feedback-01): the relay's SETUP declares MOQT_FEEDBACK; a hop
Feedback Track is taken only from a session that declared PUBLISH_FEEDBACK and subscribes
here to the media track named. Each report is mapped as D14 allows: stalls written onward
as observations, the tally held against what the relay sent (more_than_sent ends the
track), a field with no mapping refused whole (metric_unmapped). Bounds on tracks a
session, report bytes and objects.
tests/monitoring.rs; the workflow runs it again with --features qlog. Planted, each caught
for its own reason: a metrics capture one counter above its exposition (value_disagrees), a
log attribute carrying an address (address_in_attribute), qlog times descending
(time_regress) and a subgroup header not logged (object_before_header), the unmapped-metric
refusal off (metric_unmapped). Also falsified: the tally not held to what was sent, the
subscription not required, and PUBLISH into the relay's own namespaces let through.
moq-streaming-format §4.8, delivery-telemetry §11, live-metering §6.6 and D14 now say what
the relay does, with three readings recorded in D14.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
457 lines
30 KiB
Markdown
457 lines
30 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 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)).
|
||
|
||
## 11. MoQ Feedback
|
||
|
||
§11.1 `draft-liu-moq-feedback-01` (30 October 2026, read whole; written against
|
||
`draft-ietf-moq-transport-22`) has a subscriber of a media track publish MQF reports on a
|
||
Feedback Track: per object, whether it arrived whole and in time, a window's counts, and
|
||
optional metrics of the subscriber's playout and connection. A report is its consumer's to
|
||
act on, the adjacent publisher's or the original publisher's. This section states what a
|
||
consumer **writes onward** as delivery telemetry, and it adds nothing to what §2 lets a sink
|
||
report about a viewer: no member, no key and no identifier.
|
||
|
||
§11.2 Every field of the draft, and where it goes:
|
||
|
||
| field | goes | as, or why not | what it reveals where it is written |
|
||
|---|---|---|---|
|
||
| Report Timestamp | consumed | orders reports; no clock value of the publisher is written | |
|
||
| Report Sequence | consumed | a report not above the last is refused | |
|
||
| Object Entry: Group ID, Object ID, Status | metering | the tally of the track's objects by latest status ([live-metering](live-metering.md) §6.6) | which objects of the track reached the subscriber, to the hop that served it and the room's service |
|
||
| Object Entry: Receive Timestamp Delta | withheld | `local_clock`: a time on a clock of the viewer's device | |
|
||
| Summary Stats: Report Interval | consumed | | |
|
||
| Summary Stats: Total Objects Evaluated | consumed | held to the three counts' sum | |
|
||
| Summary Stats: Objects Received, Objects Received Late, Objects Lost | consumed | a window's counts of what the entries state object by object | |
|
||
| Summary Stats: Avg Inter-Arrival Delta | withheld | `no_member` | |
|
||
| PLAYOUT_AHEAD_MS | observation | `stall-start` at 0 and `stall-end` above 0 after it, with `metrics.bsa` and `metrics.bsda_ms`: `e=ps`, `sta=r`, `sta=p`, `bsa`, `bsda`, `ts` (§3) | the session's buffer starvations and their duration: its network conditions (§3.4) |
|
||
| ESTIMATED_BANDWIDTH_KBPS | withheld | `no_member` | |
|
||
| PEER_RTT_US | withheld | `no_member` | |
|
||
| PEER_LOSS_RATE | withheld | `no_member` | |
|
||
| feedback-source-id | withheld | `not_an_identity` | |
|
||
| MQF_FEEDBACK_TYPE | consumed | says which end consumes the track | |
|
||
|
||
`no_member`: an observation (§2.1) has no member for the figure, and this revision adds
|
||
none. A consumer still holds the report and may adapt by it; it does not report the figure.
|
||
|
||
§11.3 **The session.** A consumer writes its observations in a session of its own: a `sid`
|
||
fresh for the Feedback Track's publication session and derived from nothing of the publisher's
|
||
(§8.1). The feedback-source-id is in no observation, and is not read at all: the draft says
|
||
it "MUST NOT be interpreted as a user identity", and an identifier that is never read cannot
|
||
be. An observation's `ts_ms` is the consumer's own clock when the report arrived; the
|
||
report's timestamp is a monotonic clock of the publisher's and only orders reports.
|
||
|
||
§11.4 **Stalls.** A report whose PLAYOUT_AHEAD_MS is 0, in a session that is not stalled,
|
||
is a `stall-start`; the next whose value is above 0 is a `stall-end`. `metrics.bsa` counts
|
||
the stalls so begun and `metrics.bsda_ms` sums their durations by the consumer's clock, so
|
||
far; a report with no PLAYOUT_AHEAD_MS changes neither. These are the observations a sink
|
||
reports of itself (§2.1), their CMCD reports are §3's, and §1 holds of them: they are
|
||
statements of the Feedback Publisher, and no signature covers them.
|
||
|
||
§11.5 **A field with no mapping.** An Optional Metric of any type but the four of §11.2,
|
||
one the draft reserves or an application's own, has no row. A report that carries one reads,
|
||
as the draft's "MUST ignore" asks of a consumer, and nothing is written from that report:
|
||
`metric_unmapped`. A field is never passed on without a row, and never dropped without a
|
||
reason. A Metric Type twice in one report is `metric_twice`; a Report Sequence not above the
|
||
last mapped, `sequence_regress`; a report received before the one ahead of it, `time_regress`.
|
||
|
||
§11.6 **The code points.** The draft's Setup Option and Track Property are unassigned (its
|
||
TBD1 and TBD2). The reference's are placeholders in the range MOQT leaves to an application,
|
||
held in one module (`joule-moq`'s `feedback`): an unknown Setup Option is ignored and an
|
||
unknown Track Property forwarded, so neither needs a negotiation of its own. A Feedback
|
||
Track is accepted only from a peer that declared PUBLISH_FEEDBACK by an end that declared
|
||
SUBSCRIBE_FEEDBACK, and Optional Metrics only where both declared them.
|
||
|
||
§11.7 The reference is `joule-moq`'s `feedback` module: the names, the capabilities, the
|
||
report's bytes, and a consumer's mapping of a track's reports. The corpus is
|
||
[`telemetry-conformance/moq-feedback/`](../telemetry-conformance/moq-feedback/): the draft's
|
||
names and its worked report, a session's reports with what each comes to, and inputs refused
|
||
with the reasons of its README, a field with no mapping among them. `moq_feedback_verify.py`
|
||
is the second implementation, sharing no code with it: it requires of every observation the
|
||
members of §2.1 and no other, the consumer's own `sid`, and no value of a report's clock.
|
||
|
||
§11.8 **A consumer in service.** The reference relay (`joule-moq-relay`) consumes hop Feedback
|
||
Tracks where its operator turns that on. It declares SUBSCRIBE_FEEDBACK and Optional Metrics,
|
||
and accepts a Feedback Track only from a session that declared PUBLISH_FEEDBACK and holds a
|
||
subscription at the relay to the media track the name states. Each report goes through the
|
||
mapping of §11.2: the observations are written for the relay's operator, in a session id the
|
||
relay draws for that Feedback Track, and the tally ([live-metering](live-metering.md) §6.6)
|
||
stays in the relay, where a tally above what the relay sent that session on the track ends
|
||
the Feedback Track. A report with a field that has no mapping is refused whole and counted.
|
||
A Feedback Track is forwarded and cached nowhere, and an origin Feedback Track is not the
|
||
relay's to consume.
|