open-standards/wai/extensions/bearer-receipts.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

37 KiB
Raw Permalink Blame History

WAI Extension: Bearer Receipts

Status: Draft. Registers the record wai.bearer.receipts: one receipt on every bearer. A publisher's group and object receipts (jwp-receipts §2.4.1, §2.4.2) are the same bytes whether a group travels over a two-way relay chain, IP multicast or a broadcast bearer with no return path. What a bearer changes is what can honestly be said about delivery, and this extension says it beside the receipt, in two claim classes of jwp-receipts: a publisher's transmission claim (§4, jwp-receipts §2.4.14), which declares the bearer a group was sent over and attests transmission, never delivery; and a sink's reconciliation claim (§5, jwp-receipts §2.4.15), signed later and out of band, which attests the objects the sink received by a Merkle root under the group's own tree. A verifier derives from them what a relying party may state about a group (§6), an upper bound on each receiver's share of a shared transmission's energy (§7), and confirms keys on a bearer with no return path under anchors provisioned in advance (§8). §9 maps the carriage onto the bearers. WAI defines no transport, here as elsewhere (moq-streaming-format). The reference implementation is the bearer_receipts module of wai-rs (feature bearer_receipts), with the two classes' bodies in claim_receipt::bearer; the corpus is live-conformance/bearers/, and its second implementation is live-conformance/bearer_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.

Texts read for this revision, each at the edition named: ETSI TS 103 769 V1.2.1 (2024-11); ETSI TS 103 770 V1.2.1 (2024-09); ATSC A/331:2026-04, A/333:2026-04 and A/360:2026-08; 3GPP TS 26.346 V19.3.0, TS 26.517 V19.3.0 and TS 26.502 V19.6.0; RFC 6726; draft-ramadan-moq-multicast-00 and draft-ietf-moq-transport-22, Internet-Drafts; ETSI TS 102 796 V1.8.1 (2026-09) and ETSI TS 102 809 V1.3.1. A clause number of one of them in this text is that edition's.

1. Scope

In scope: the bearer classes (§2); why the bearer is declared beside the receipt and not in it (§3); the transmission claim (§4) and the reconciliation claim (§5), their bodies, rules and context checks; the verdict a verifier derives for one group from its receipts and those claims (§6); the upper bound on a receiver's share of a shared transmission's energy (§7); verification with no return path (§8); and the mappings onto bearers (§9).

When each object passes on a bearer with no return path, and with which forward error correction, is carousel's: a publisher's schedule claim beside the transmission claim here, from which a verifier derives when a receiver within a loss bound holds each object.

A node that sends again, over a bearer instance of its own, objects it holds of a group another key signed, declares that instance in a cache-transmission claim, and reconciliations over it are counted beside the publisher's instances: cache-authority §5, §7.

Out of scope: a transport or a bearer; a reception-reporting protocol — a reconciliation travels on whatever path a sink later has, as a receipt file (http-delivery §8.1) or inside a bearer's own reporting (§9); a pre-emption authority (unidirectional-delivery §3 registers none, and nor does this extension); and any statement of rendering. No receipt over a bearer with no return path can prove rendering, and nothing here attempts it.

Relationship to the core spec: wai.bearer.receipts is a record (SPEC §5): the transmission and reconciliation claims are signed records a WAI tool emits and verifies. It names no payload format and is not dispatchable.

2. Terms

  • Bearer: what carries a group's objects from a publisher to sinks. Its class is one of:
    • bidirectional: each sink is reached over a path that can carry its statements back, such as a relay chain or unicast HTTP;
    • multicast: one transmission reaches every sink that joined it, with no per-sink path on the bearer itself; a sink may have another path;
    • unidirectional: one transmission, and no path back from any sink.
  • Bearer instance: one transmission a publisher made, or a node that sends again what it holds (cache-authority §5), named by an absolute URI, its bearer_id: a multicast session, a broadcast service, a relay chain's origin. WAI reads nothing from it and compares it byte for byte.
  • Transmission: putting a group's objects and their receipts onto a bearer instance. Delivery: a sink receiving them. A transmission is not a delivery, on any bearer.
  • Sink: a receiver that verifies what it received under jwp-receipts §3. Relying party: anyone else who reads a group's receipts and claims.
  • Group, link hash, leaf, root: as jwp-receipts §2.2, §2.4.1 and §3 step 3. delivered-verified and inclusion-verified: as jwp-receipts §3.

3. One receipt on every bearer

The group receipt signs its objects' root, its coordinates, its times and its publisher (jwp-receipts §2.3). It names no bearer, and this extension adds no member to it. The bearer is declared beside the receipt, in a transmission claim signed by the group's own key and naming the group by its link hash (§4). Three facts fix that form:

  • A group is not tied to one bearer. A group may be sent over a relay chain and a broadcast at once, and carried again later by another. A bearer inside the signed payload would make one group per bearer, each with its own link hash, and the chain of a track (jwp-receipts §3 step 3) could link through only one of them.
  • The bearer is a fact about a transmission, not about the bytes. The receipt attests the bytes; a declaration attests what the publisher did with them, and is a statement of its own with its own signature.
  • Every receipt already issued keeps verifying. A group carried before this extension, or carried by a party that does not use it, verifies exactly as before; what is missing is a declaration, which is reported as missing (§6.5), never inferred.

So the "MUST declare the bearer" of unidirectional-delivery §4 is met by a signed declaration that changes no byte of the receipt it declares. A sink that holds a group's objects and receipts verifies them under jwp-receipts §3 the same way on every bearer and reaches the same verdicts (delivered-verified for each object whose bytes it holds): the corpus carries one group, byte for byte, on all three classes (§11). What differs between bearers is what anyone other than that sink may state about delivery (§6), and how a receiver's share of the transmission's energy is bounded (§7).

4. The transmission claim (class: "transmission")

A publisher that sends a group over a bearer MAY declare the transmission in a transmission claim, one per bearer instance, and MUST where unidirectional-delivery §4 requires it. Its subject_type is receipt and its subject the group receipt's link hash (jwp-receipts §3 step 3). It is signed by the key that signed the group. It attests that the publisher transmitted the group over the bearer instance it names, and nothing about what any sink received.

4.1 Body

member type meaning
bearer string The bearer's class: bidirectional, multicast or unidirectional (§2).
bearer_id absolute URI The bearer instance (§2).
energy object or null The energy the bearer spent transmitting the group on this instance, every repetition included, written as jwp-receipts §2.4.12 writes a session's energy: { "joules_micro", "energy_provenance", "energy_uncertainty", "energy_counter_filtering", "energy_calibration_ref" }. null when the transmission was not metered, never a figure of 0 (energy-measurement §2). It is not the group's joules_total, which is the energy of producing the objects.
attests string MUST be "transmitted". Any string passes step 2; another value is refused at step 4.

bearer is read at jwp-receipts §3.1 step 2 against its enumeration (field_type), and bearer_id as an absolute URI.

4.2 Rules

Checked at jwp-receipts §3.1 step 4, in this order:

  1. attests MUST be "transmitted" (transmission_not_transmitted): a declaration that says it attests anything more is refused, never read as a weaker one.
  2. Where energy is not null, its joules_micro MUST NOT be 0 (energy_zero_figure), and its class is admitted as jwp-receipts §2.4.5 rule 2 admits one (energy_unknown_acquisition, energy_legacy_acquisition, energy_acquisition_evidence).

4.3 Against the group

Checked at jwp-receipts §3.1 step 6 against the group receipt the verifier holds, which has passed jwp-receipts §3 steps 1 and 2 (context_invalid otherwise; another receipt is context_mismatch). In order:

  1. the group's link hash is the claim's subject (transmission_subject_mismatch): a declaration over another group declares nothing about this one;
  2. the claim's signer_pubkey is the group's (transmission_signer_mismatch): only the publisher declares its own transmission.

A transmission claim checked without the group stops at step 5 and is not counted by §6.

4.4 Authority

Confirmed under jwp-receipts §7, a transmission claim needs the authority transmission, which is not pinned-only and is not delivery. Its energy figure, where energy is not null, is a second item, as a session's is (jwp-receipts §7.5): it needs transmission and the energy authority of its class, so a hardware-class figure is confirmed only under a pinned anchor, and a verifier reports the declaration's confirmation and its figure's side by side.

5. The reconciliation claim (class: "reconciliation")

A sink that received objects of a group MAY later sign which ones, in a reconciliation claim. Its subject_type is receipt and its subject the group receipt's link hash. It is signed by the sink, never by the publisher. It is not a hop: it is never a group's parent, never in the publisher's chain, and nothing in the publisher's chain changes when it arrives.

5.1 Body

member type meaning
bearer string The class of the bearer the sink received over (§2).
bearer_id absolute URI The bearer instance it received over: the bearer_id of the publisher's transmission claim, or of a node's cache-transmission claim, for that instance.
leaf_count integer The group's leaf_count.
received hex A bitmap of ⌈leaf_count / 8⌉ bytes. Object i is bit 0x80 >> (i mod 8) of byte ⌊i / 8⌋, so the hexadecimal reads left to right in object order; the bit is set exactly when the sink accepted object i under jwp-receipts §3 steps 1–6 over its bytes.
received_root 32 B hex The root jwp-receipts §2.2 computes over the leaves of the received objects, in increasing leaf_index, each leaf in the form its object receipt takes (legacy or labelled). With every bit set it is the group's root_hash.
attests string MUST be "received". Any string passes step 2; another value is refused at step 4.

The root is a commitment under the group's own tree, so it is checked against the publisher's root without the objects' bytes (§5.3).

5.2 Rules

Checked at jwp-receipts §3.1 step 4, in this order:

  1. attests MUST be "received" (reconciliation_not_received).
  2. leaf_count is at least 1, received holds exactly ⌈leaf_count / 8⌉ bytes, no bit at or past leaf_count is set, and at least one bit is set (reconciliation_bitmap_malformed). A sink that received nothing has nothing to reconcile.

5.3 Against the group and its objects

Checked at jwp-receipts §3.1 step 6 against the group receipt the verifier holds, which has passed jwp-receipts §3 steps 1 and 2 (context_invalid, context_mismatch as §4.3), and the object receipts it holds under that group, each of which has passed jwp-receipts §3 step 5 — verified by inclusion at least. In order:

  1. the group's link hash is the claim's subject (reconciliation_subject_mismatch);
  2. the claim's signer_pubkey is not the group's (reconciliation_by_publisher): a publisher's statement that it received its own objects is not a reconciliation;
  3. leaf_count is the group's (reconciliation_leaf_count_mismatch);
  4. with every bit set, received_root is the group's root_hash; otherwise the verifier holds an object receipt at every set bit (reconciliation_objects_not_held, recoverable by obtaining them, never accepted silently) and received_root is the root over their leaves (reconciliation_root_mismatch). A root over a leaf that is not the group's at its position — a hash not in the group — is refused here.

A reconciliation checked without the group stops at step 5 and is not counted by §6.

5.4 What it attests

An accepted reconciliation establishes that its signer says it received the objects its bitmap names, with the bytes their receipts commit to. It does not establish that the signer held those bytes: content hashes travel with the receipts, so a party that saw only the receipts can sign one. It never attests presentation, rendering or timeliness. Like a binding-recovery record (jwp-receipts §2.6.3), it attests less than a hop would, and says so in-band (attests).

5.5 Authority

Confirmed under jwp-receipts §7, a reconciliation needs the authority reconciliation, which is not pinned-only and is not delivery. A sink signs under an origin's key where its origin speaks for it, and under a did:key otherwise, which is unconfirmed (jwp-receipts §7.4 step 1), as a sink's reconstruct-session claim is.

6. The bearer verdict

What a verifier may state about one group, from its receipts and the claims about it.

6.1 Inputs

The group receipt; the object receipts the verifier holds under it, each with the object's envelope bytes where it holds them; the transmission and reconciliation claims it holds about the group, each signer's in the order of its chain; optionally, for a verifier that is itself a sink, the bearer class and instance it received the group over (§6.5); optionally, a statement offered about the group (§6.4); and optionally, for a relying party that counts receivers only by keys it has confirmed, the set of those keys (§7).

6.2 Steps

  1. The group. jwp-receipts §3 steps 1 and 2. A group that fails is refused with that step's reason, and nothing below is derived from it. (Its chain is jwp-receipts §3 step 3's, and unchanged.)
  2. The objects. Each object receipt is checked against the group: with the envelope, by jwp-receipts §3 steps 4–6 (delivered-verified); without it, by step 5 (inclusion-verified); or refused with its step's reason. These verdicts do not depend on the bearer, the declarations or the reconciliations.
  3. The claims. Each claim, in order, under jwp-receipts §3.1 steps 1–5, each signer's previous claim being the last of its claims held before it here; then step 6, a transmission claim by §4.3 and a reconciliation by §5.3 against the object receipts that passed step 2. A claim of another class is context_mismatch at step 6. A claim refused at any step takes no further part.
  4. The declarations. The accepted transmission claims, by bearer_id. Two with one bearer_id and different bodies set aside every declaration of that instance (bearer_contradicted): a publisher that contradicts itself about one transmission has declared neither. Two with the same body are one declaration.
  5. The reconciliations, each accepted one in order. It is set aside, and counted nowhere, at the first of these that holds: step 4 set aside the declarations of its bearer_id (bearer_contradicted); no declaration has its bearer_id (bearer_not_declared); the declaration names another bearer (bearer_contradicted); the verifier counts only confirmed keys and its key is not among them (sink_not_confirmed). Otherwise it is counted for its bearer instance. A second counted reconciliation of one key for one instance adds no sink (reconciliation_duplicate_sink): its bits are joined to the first's.
  6. The counts. sinks: the distinct keys with a counted reconciliation; whole: those whose joined bits, over every instance, cover every object of the group; for each object, the number of distinct keys whose joined bits hold it; and for each declaration, the distinct keys counted for its instance.

6.3 Status

  • published: no declaration stands. The group verifies; nothing is stated about where it went.
  • transmitted: at least one declaration stands, and no reconciliation is counted. The publisher sent it over the bearers declared; nothing is stated about any sink.
  • reconciled: at least one reconciliation is counted. sinks keys say they received objects of the group, out of band and on their own authority, and whole of them say they received every object.

A verifier MUST NOT report a group, or anything read from it, as delivered to anyone but itself on the strength of the publisher's receipts and declarations: the receipts attest publication, the declarations transmission. delivered-verified stays the verdict of a sink over bytes it holds (jwp-receipts §3).

6.4 Statements offered about a group

A verifier MAY be asked to check a statement made about the group:

  • transmitted over a bearer class: holds when a declaration of that class stands, and is otherwise refused (bearer_not_declared);
  • delivered to k sinks (k at least 1): holds when whole is at least k. With whole 0 it is refused (delivery_not_reconciled), whatever was declared: a transmitted-only group presented as delivered is refused here. With whole from 1 to k − 1 it is refused (delivery_exceeds_reconciled).

6.5 A sink's own bearer

A sink knows the bearer instance it received over. Beside its object verdicts, and never changing them, it reports the group's declaration for that instance: declared when one stands with the sink's class; bearer_contradicted when one stands with another class, or step 4 set the instance's declarations aside; and bearer_not_declared when none names the instance. A group received over a unidirectional bearer with no declaration is unidirectional-delivery §4's missing declaration.

7. Energy per receiver on a shared transmission

A transmission's energy is spent once, whoever receives it. On a multicast or broadcast bearer the number of receivers is not known, and unknown is not zero: no figure divided by an assumed audience is a measurement of anyone's share. What a verifier states instead is an upper bound, for each declaration of §6.2 step 4 whose energy is not null:

per_receiver_upper_bound = ⌈ joules_micro / max(1, n) ⌉

where n is the number of distinct keys counted for that instance (§6.2 step 6).

  • It bounds an allocation, not a reading. Dividing one transmission's figure among its receivers is an allocation of that figure (as energy-steering §3.3 says of a hop shared by pathways); no receiver's own energy was read. A verifier MUST report it as a bound, named so, and MUST NOT report it as any receiver's measured energy.
  • Why it is an upper bound. Every receiver of the transmission shares the one figure. If the counted keys are distinct receivers, at least n receivers shared it, and an equal share is at most joules_micro / n; with no reconciliation, a receiver's share is at most the whole figure. Rounding up keeps it a bound in integer microjoules. Each reconciliation counted for the instance tightens it, and none raises it.
  • Its class is the figure's. The bound is stated with the transmission figure's acquisition class and evidence (energy-measurement §2); the division adds no measurement and changes no relative uncertainty. Bounds of two instances, or of two classes, are never added.
  • No figure, no bound. Where energy is null the verifier states no bound at all, never a bound of 0 (energy-measurement §2).
  • Keys are not receivers. A receiver that reconciles under several keys is counted once for each and lowers the bound below its share. The count is a count of keys, and a verifier reports it as one. A relying party that needs a bound per receiver counts only keys it has confirmed (jwp-receipts §7.4) with the authority reconciliation under an origin it relies on to give that authority to one key per receiver, and reports that it did (§6.2 step 5, sink_not_confirmed).
  • It is not the group's production energy: joules_total (jwp-receipts §2.4.1) and its objects' figures are read as jwp-receipts reads them, and never added to a transmission's.

8. Verification without a return path

8.1 Anchors

A sink on a bearer with no return path cannot retrieve a key log from an origin's location (jwp-receipts §7.3, http-delivery §8.4): first-use retrieval is unavailable to it. It confirms under anchors it already holds — logs provisioned in advance by its relying party (pinned), or logs it retrieved earlier over another path (fetched, with the time it retrieved them). The pinned-only authorities, and any authority registered for pre-emption, are confirmed only under a pinned anchor that has not moved (jwp-receipts §7.4 step 6a), on this bearer as on any other.

8.2 Logs that arrive on the bearer

A key log that reaches a sink over the bearer — in a file carousel, a delivery session, a multicast track — is neither pinned nor fetched, whatever it holds. The sink:

  • reads it as jwp-receipts §7.3 reads a log, and holds its revisions, each checked against its parent when §7.4 step 3 walks them, so a revocation or a rotation its origin sends over the bearer reaches a sink that has the origin anchored;
  • MUST NOT anchor an origin from it (anchor_on_bearer): an item under an origin the sink has not otherwise anchored stays unconfirmed (origin_not_anchored). A trust anchor delivered over the bearer it authorises is not a trust anchor (unidirectional-delivery §3), for receipts as for pre-emption.

A revision on the bearer that its anchored line does not authorise — another root, a log written by whoever controls the bearer — is no child of the trusted line (jwp-receipts §7.2 rule 7) and confirms nothing.

8.3 The procedure

Given the item to confirm (a group receipt with its object receipts, or a claim), the logs the sink was provisioned with and the logs that arrived on the bearer:

  1. verify the item — the group under jwp-receipts §3 steps 1 and 2 and each object under step 5; a claim under §3.1 steps 1–4 — and stop with its reason if it fails;
  2. anchor each provisioned log as pinned (jwp-receipts §7.3), holding its revisions; a log that does not read anchors nothing, with its reason;
  3. read each log that arrived on the bearer as jwp-receipts §7.3 reads one and hold its revisions; report it held where its origin is anchored by step 2, and anchor_on_bearer otherwise. A log that does not read is held in no part, with its reason;
  4. confirm the item under jwp-receipts §7.4 against every revision held and the anchors of step 2, and, for a transmission claim with an energy figure, its figure as a second item (§4.4).

The procedure touches no network and no clock; sinks holding the same bytes reach the same verdicts, offline, on every platform. A confirmation describes the log as the sink holds it: a revocation that has not reached it is not in it, and the head's revision_hash it reports says how current that was.

9. Bearer mappings

9.1 What WAI fixes on every bearer (normative)

Where a bearer carries files or objects, WAI controls these bytes:

  • Envelopes. Each object is one WAI envelope, carried whole as one file, object or segment of the bearer; its bytes are the ones jwp-receipts §2.1 hashes. The bearer's own framing — packets, forward error correction, an object's encoding for transport — is outside them, and a sink hashes the envelope it reconstructs.
  • Receipts and claims. A group receipt and every claim — transmission and reconciliation claims among them — travel as the receipt files of http-delivery §8.1, under the same name relative to the objects' location (rcpt/<link hash>.json), with the same bytes and the same limits, and a sink reads each as §8.1 reads one. An object's receipts travel in its receipt index (http-delivery §8.2). On a bearer that carries tracks rather than files, the group and object receipts ride the receipts track (moq-streaming-format §4.1); this revision defines no carriage of claims on a track, so claims travel as receipt files on whatever file path the sink has.
  • The declaration rides with the group. A publisher that declares a transmission SHOULD send the transmission claim on the same bearer instance and in the same session as the group's receipts, so that a sink receives the declaration that covers what it received.
  • Key logs. A key log a bearer carries is the document of jwp-receipts §7.2, at the path /.well-known/wai-keys relative to the origin it names, and anchors nothing (§8.2).
  • Reconciliation. A sink sends its reconciliations, when it has a path, as receipt files; where the bearer's own reporting carries files or opaque payloads, it MAY carry them there. A reconciliation is the same claim whatever carried it.

The mappings below are informative: each says where these ride in the bearer's own structures and what path back it defines.

9.2 FLUTE (RFC 6726)

Each envelope, receipt file and receipt index is one FLUTE file, named in the File Delivery Table by its Content-Location, with Content-Type and, where the sender gives it, Content-MD5 (§3.4.2). The table admits further attributes (§3.4.2), and WAI defines none: everything a sink checks is in the files. FLUTE is a unidirectional delivery service with no feedback (§7.4), so a transmission over it is declared unidirectional unless another standard adds a path back, and reconciliation goes over another path or none.

9.3 DVB multicast ABR (ETSI TS 103 769)

The multicast server carries each object as a transport object of FLUTE (Annex F) or ROUTE (Annex H), and the gateway serves it to players over HTTP. Envelopes ride as the media objects; receipt files, receipt indexes and key logs ride as the "other resources" a ResourceLocator carousels in the same session (clause 10.2.3.14.3; ancillary objects, clause 8.3.4.4). Unicast repair (clause 9) and service reporting (clause 11: a JSON report posted to /dvb/mabr/reportingInformationInstance) are paths back where the deployment has them; a report's object-delivery event carries a status, not the object (Table 11.1.2.2-1), so a reconciliation, which names objects, travels beside it as a receipt file. A deployment with both paths declares multicast; one without, unidirectional. The integrity and authenticity the specification adds (clause 12: content digests and HTTP message signatures) are the gateway's checks of transport objects and change no WAI byte.

9.4 DVB-I (ETSI TS 103 770)

A service list signals the instances of a service — among them multicast transport streams and DASH (clause 5.5.18) — and an identifier-based instance whose locator is an opaque URI (clause 5.5.18.8). It carries no objects of its own and defines no path back. Where the service list gives an instance a URI — an identifier-based locator, or a DASH presentation's address — a publisher's bearer_id for that instance SHOULD be that URI, so a sink reading the list finds the declaration by it.

9.5 ATSC 3.0 (A/331)

ROUTE carries envelopes as file-mode objects described by the Extended FDT, or in entity mode (Annex A.3.3.2, A.3.3.3); receipt files, receipt indexes and key logs ride as locally cached non-real-time files in the same ROUTE session, with relative Content-Location (clause 8.1.1.2), or inside a signed package (Annex A.3.3.5). A/331 requires its signalling tables to be signed (clause 5.9; A/360:2026-08 clause 5.2.2); that signs the signalling, not the media files, and is the broadcaster's statement, not the publisher's. Service usage reporting (A/333:2026-04) reports consumption by service and interval, not by file or object, and is unsigned; byte-range repair over broadband (A/331 clause 8.3) is a path back where the receiver has broadband. A receiver with broadband sends reconciliations as receipt files; the broadcast transmission is declared unidirectional, and the broadband path, where a group is also served over it, is a bidirectional instance of its own.

9.6 3GPP MBMS and 5G multicast-broadcast (TS 26.346, TS 26.517)

MBMS download delivery carries each file over FLUTE with its own Content-Location (TS 26.346 clause 7.2); 5G multicast-broadcast user services carry objects by their object distribution method, on the same download profile, in single, collection, carousel or streaming mode (TS 26.517 clause 6.2), and envelopes, receipt files, indexes and key logs ride as objects of the session. File repair (TS 26.346 clause 9.3; TS 26.517 clause 6.2.4) is a path back. MBMS reception reporting (TS 26.346 clause 9.4) lists each file received by its fileURI, with Content-MD5 where the table carried it, and is unsigned: it names files, not objects of a group, and a reconciliation travels beside it. TS 26.517 defines no reception report, and TS 26.502 leaves one for further study (clause 4.2.5). A session whose receivers report declares multicast; one without a path back, unidirectional.

9.7 MoQ over multicast (draft-ramadan-moq-multicast-00)

The draft maps MoQ tracks onto source-specific or any-source multicast endpoints listed in a catalog extension, each datagram carrying one MMTP packet, with a MoQ unicast session first and unicast failover and repair (§1.1, §4, §6). The receipts track rides as any track; a sink that holds the unicast session is reached multicast with a path back, and one that only joined the multicast group is reached unidirectional. The draft's own signed per-group manifest (§7.2) is the draft's, beside the receipts; WAI reads nothing from it. No MoQ working-group draft (draft-ietf-moq-transport-22 among them) defines multicast at this revision.

9.8 HbbTV (ETSI TS 102 796)

An HbbTV application reads files of a broadcast DSM-CC object carousel (ETSI TS 102 809 clause 7.1, ETSI TS 102 796 clause 7.2.2) and fetches over broadband (clause 7.3.2). V1.8.1 (HbbTV 2.0.5) adds WebAssembly for applications (clause A.3.28, Annex R), so a sink built for WebAssembly runs inside an application. Envelopes, receipt files and key logs ride as carousel files; the carousel is unidirectional, and the application's broadband path is the path back for reconciliations. A key log carried in the carousel is not an anchor (§8.2): the anchor is provisioned with the application, by its relying party. The carousel's own signing (ETSI TS 102 809 clause 9) is optional in HbbTV and signs the carousel, not the publisher's objects.

10. Reasons

The codes this extension adds, each with where it is raised:

reason raised at
transmission_not_transmitted §4.2 rule 1
transmission_subject_mismatch §4.3 step 1
transmission_signer_mismatch §4.3 step 2
reconciliation_not_received §5.2 rule 1
reconciliation_bitmap_malformed §5.2 rule 2
reconciliation_subject_mismatch §5.3 step 1
reconciliation_by_publisher §5.3 step 2
reconciliation_leaf_count_mismatch §5.3 step 3
reconciliation_objects_not_held §5.3 step 4
reconciliation_root_mismatch §5.3 step 4
bearer_contradicted §6.2 steps 4 and 5, §6.5
bearer_not_declared §6.2 step 5, §6.4, §6.5
sink_not_confirmed §6.2 step 5
reconciliation_duplicate_sink §6.2 step 5
delivery_not_reconciled §6.4
delivery_exceeds_reconciled §6.4
anchor_on_bearer §8.2, §8.3 step 3

A group or object also raises the codes of jwp-receipts §3.3, a claim every code of jwp-receipts §3.1 steps 1 to 5 and those of step 6 named in §4.3 and §5.3, and a confirmation the codes of jwp-receipts §7.7.

11. Conformance

live-conformance/bearers/ holds the corpus, written from fixed seeds by wai_bearer_vectors gen as vectors.json. It has:

  • One receipt on every bearer: one group and its object receipts, byte for byte, with a declaration of each class, verified by a sink holding the bytes; the object verdicts are the same in every case, and the group's bytes are checked equal across them.
  • Verdicts: groups published, transmitted and reconciled; a group simulcast on two instances, each with its own count and bound; partial and whole reconciliations, a labelled group among them; a duplicate sink, joined; a relying party counting confirmed keys only.
  • Energy: bounds before and after reconciliations arrive, an unmetered transmission with no bound, and a hardware-class figure.
  • Negative controls, one for each reason of §10 that a verdict or a claim raises: a transmitted-only group presented as delivered; a partial reconciliation presented as delivered to a sink; a reconciliation over a leaf that is not the group's; one signed by the publisher's key; one whose objects are not held; a declaration over another group, one signed by another key, and two that contradict each other; a reconciliation over an undeclared instance; a sink whose bearer was not declared; and malformed bodies.
  • Offline: confirmation under a pinned anchor with no network; a revocation that arrives on the bearer; a log forged on the bearer for a pinned origin; a log that arrives on the bearer for an origin not pinned (anchor_on_bearer); and a hardware-class transmission figure confirmed beside its declaration.

A conforming verifier gives every case the outcome, the reason and everything else the corpus states. wai_bearer_vectors verify and the test tests/bearer_receipts.rs check that the file regenerates byte for byte, that wai-rs gives every case its outcome, that every reason of §10 appears, and that §10 lists exactly the reference's codes. live-conformance/bearer_verify.py is the second implementation, written from this text: it shares no code with wai-rs; it verifies groups and objects with the JWP corpus's second implementation (jwp-conformance/jwp_verify.py), claims through steps 1–5 with the claim corpus's (claim-conformance/claim_verify.py), and confirmations with the key-discovery corpus's (signer-conformance/signer_verify.py), and writes §4.3, §5.3, §6, §7 and §8 itself; and it mutates copies of the corpus and requires every copy to fail.

12. Security and privacy considerations

  • A declaration is the publisher's word. Nothing checks that a bearer instance exists or that its class is the one declared. A false bidirectional gains nothing here: no class of bearer makes a group delivered (§6.3).
  • A reconciliation is the sink's word (§5.4). Keys are cheap; a party that wants a group to look widely received signs many reconciliations. That raises sinks and whole and lowers the bound, and both are reported as counts of keys (§7). A relying party that needs more counts confirmed keys only.
  • Replay. A reconciliation names the group by its link hash, so it cannot be moved to another group; within one group a second one of a key adds nothing (§6.2 step 5).
  • What a sink discloses. A sink on a broadcast bearer is unobserved: unidirectional-delivery §2's property is that the source learns nothing. A reconciliation gives that up for the groups it names, to the distributor that receives it. Reconciling is part of the delivery service a distributor operates over WAI, and this extension sets no consent rule of its own for it. Where a regime asks the distributor for a lawful basis, the distributor records it as a lawful-basis claim (jwp-receipts §2.4.6). A sink MAY sign each reconciliation with a fresh did:key, which unlinks its reconciliations and leaves each group's count unchanged.
  • A bearer that lies about keys. A log on the bearer anchors nothing (§8.2), so whoever controls the bearer can withhold revisions — a revocation among them — but cannot add a key to an anchored origin. A sink that has had no path for long holds a stale log, and its confirmation reports the head it used.