open-standards/wai/extensions/receipt-ledger.md
Transaction Science 8a409172f9
All checks were successful
joule-moq-remote host agent / joule-moq-remote · does this change touch it (pull_request) Successful in 7s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 6s
wai-app and Create / wai-app · does this change touch it (pull_request) Successful in 5s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 7s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 6s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 13s
wai-audit collector and player integration / does this change touch it (pull_request) Successful in 8s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 7s
WAI operation catalogue / does this change touch it (pull_request) Successful in 8s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m47s
WAI energy receipts / does this change touch it (pull_request) Successful in 12s
WAI conformance / does this change touch it (pull_request) Successful in 13s
wai-fleet service / does this change touch it (pull_request) Successful in 13s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 14s
wai-key-proxy / does this change touch it (pull_request) Successful in 7s
wai-ledger service / does this change touch it (pull_request) Successful in 7s
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 10s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 11s
wai-quantum docs, course and locales / does this change touch it (pull_request) Successful in 7s
wai-quantum for JavaScript / does this change touch it (pull_request) Successful in 7s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (pull_request) Successful in 30s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 10s
wai-sink-android native sink library / does this change touch it (pull_request) Successful in 10s
wai-sink-swift native sink package / does this change touch it (pull_request) Successful in 11s
WAI Studio / does this change touch it (pull_request) Successful in 10s
wai-web wasm32 / does this change touch it (pull_request) Successful in 9s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m39s
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (pull_request) Successful in 3m12s
wai-audit collector and player integration / collector · linux (pull_request) Successful in 1m1s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 1m37s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 5m30s
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 2m22s
WAI operation catalogue / the catalogue and every door · linux (pull_request) Successful in 5m5s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 29s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 10m32s
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m37s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m21s
wai-fleet service / service · linux (pull_request) Successful in 2m6s
wai-key-proxy / proxy · linux (pull_request) Successful in 1m18s
wai-ledger service / service · linux (pull_request) Successful in 2m5s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (pull_request) Successful in 1m42s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 13m58s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 3m25s
wai-pack / wai-pack · linux (pull_request) Successful in 2m1s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (pull_request) Successful in 9s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 4m24s
wai-quantum docs, course and locales / the course and the command line's languages · linux (pull_request) Successful in 2m29s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 16m36s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 10m18s
wai-quantum for JavaScript / javascript · linux (pull_request) Successful in 3m16s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 1m1s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 4m24s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 1m9s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 8m50s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 33s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 5m57s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 6m30s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 11m16s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 7m59s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 1m34s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 12m30s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Successful in 22m48s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 18m55s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 21s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 55s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 11m30s
wai-fleet service / service · macos (pull_request) Successful in 35s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 24m43s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 36m14s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 10m27s
wai-ledger service / service · macos (pull_request) Successful in 51s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 5m47s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m59s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 8m14s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 27m33s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 3m7s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m41s
wai-pack / wai-pack · macos (pull_request) Successful in 9m35s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 4m17s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 5m23s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 2m4s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 50m58s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 2m38s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 10m2s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 10m15s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 12m0s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 7m44s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 40m4s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h45m21s
receipt-ledger: tariff version 6 prices an endpoint and a third party's usage, and bills an upscale and a tone mapping by what re-derives them
Two providers of one capability have different list prices, and one price
per capability overprices one or underprices the other. A job price now names
an endpoint, or none for every endpoint. A price of any endpoint beside a
price of one is refused: it would bill one job at both.

A third party's hop is billed by what its record states the provider bills
(route-record §6.2), in a new unit, `usage`, at the price of its endpoint and
its unit. Only the billed hop's record reaches a line: what a provider billed
for a failed hop is what the router's operator owes, and no line reads it. A
record's `charge` reaches no line either, since a line adds jobs and a
provider's figure is in its own terms; the line cites the record, which
states it signed. The proposal to put it on the line is dropped.

An upscale and a tone mapping get their own output forms. Each record is
proved by its own extension's verifier from what it read, and what the hop
delivered is held to what that proves, so the subpixels billed are what the
derivation gives. Their records are evidence, as an output receipt is, and a
new hop's run may cite them again.

These three moves share one version, so a buyer signs one new tariff and not
two. The tariff of versions 1 to 5 reads and bills as before.

The corpus's `invoice_version` control used version 6 as unknown, and now
uses 7. The fixtures are small enough that the corpus's debug test takes as
long as it did without them, about 11.5 minutes on the Framework, and the
README's commands use a release build, which regenerates it in about a minute.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 22:40:02 -05:00

1099 lines
83 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

# WAI Extension: Receipt Ledger
> Status: Draft. Registers the record `wai.delivery.ledger`: invoice lines a customer can
> recompute from the signed records WAI already defines. A seller and a buyer both sign a
> **tariff**, `WLT1`: one currency, and one price for each unit those records state, the
> units of a room session ([live-metering](live-metering.md) §4.4), the acknowledged bytes of
> each object class ([settlement](settlement.md) §3.3), the packages, entries and stored bytes
> of a package receipt ([packaging](packaging.md) §9), the counted impressions of a
> `wai.video.ssai` receipt of the break form ([live-ads](live-ads.md) §8,
> [ad-presentation](ad-presentation.md) §9.2) and the presentations of ads another party
> stitched ([stitched-presentation](stitched-presentation.md) §7.4); and the **scope** it
> covers, the rooms, the delivering parties, the packagers, and the ad deciders and stitchers
> whose records are billed under it. The
> ledger verifies each **source** by the extension that defines it, and the seller signs an
> **invoice**, `WLI1`, for a period: the records it was computed from, one line per source,
> unit and price, each line citing the records it came from, and the joules those records
> state, per scope and per acquisition class with their largest declared uncertainty, never
> summed across classes or scopes. Invoices under one tariff form a chain, and no record is
> billed in two of them. A verifier given the tariff, the invoice, the invoice before it and
> the sources recomputes every line and refuses an invoice that does not recompute. Beside the
> invoice, the ledger states two **reports**: the ad decisions reconciled against the players'
> presentation claims (for a stitched stream, stitched-presentation §7.5's own statement), and
> the delivery of each party under the one tariff with the bytes two parties were acknowledged
> for twice. A tariff of version 2 also prices **media jobs** (§4.5): a job is billed from
> its route record ([route-record](route-record.md)), its hops' OER/2 receipts and WAI's
> receipt of its output, in the units those receipts state. A tariff of version 3 prices a
> job by its capability **and its operation** (§4.6), since an encode and a decode spend
> different energy, and bills a job's **work** once, its route record and its hops' receipts,
> its output's receipt being the evidence a re-run may cite again (§6.9). A tariff of version 4
> also bills what the rest of the work spends (§4.7): a room's bytes out, which a room-session
> receipt of version 2 states (live-metering §4.4), at a price per 10^9 bytes; a package's work
> once per run that made it, by the work units it states, its receipt the product each run
> cites; a package's storage by byte-days for each period it is held; and its invoices state
> every hop's energy, the hops not billed in a scope of their own (§6.6). A tariff of version 6
> also prices a job **per endpoint**, and by the **usage** a third party's hop record states
> ([route-record](route-record.md) §6.2), and bills an upscale and a tone mapping by the subpixels
> their records state, each re-derived from what it read (§4.9). Whatever the version,
> a job's output receipt is of an object its served hop states it delivered, where its route
> record states it (§5.1), and a route record's routing figures are never charges. No new transport, no payment
> and no tax: an invoice is the recomputable figures a payment rests on. The reference implementation is the
> `receipt_ledger` module of `wai-rs` (feature `receipt_ledger`) and the service
> `wai/wai-ledger`; the corpus is [`live-conformance/ledger/`](../live-conformance/ledger/),
> and its second implementation is `live-conformance/receipt_ledger_verify.py`.
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
>
> Texts read for this revision: the WAI texts this one cites; ISO 4217 (the alphabetic
> currency code); RFC 8032; RFC 8259; FIPS 180-4.
## 1. Scope
In scope: how one tariff prices rooms, packages, HTTP and relayed delivery and ads together
(§3); the tariff, its two signatures and its scope (§4); the sources a ledger is given and what
each contributes (§5); the invoice, its records, lines, citations and energy, its signature and
its chain (§6); verification (§7); the two reports (§8); and the reference service (§9).
Out of scope: a transport; payment, a payment rail, tax and any contact with a party outside the
records; how a seller chooses its prices; how a party measures its energy, which
[energy-measurement](energy-measurement.md) governs; and the records themselves, which their own
extensions define, verify and leave as they are. The live-metering tariff and the settlement
statement's tariff keep their meaning: this extension reads them, and changes neither.
Relationship to the core spec: `wai.delivery.ledger` is a **record** ([SPEC](../SPEC.md) §5):
a signed record a WAI tool emits and verifies. It names no payload format and is not
dispatchable.
## 2. Terms
- **Seller**, **buyer**: Ed25519 keys (RFC 8032). The seller operates the ledger and signs its
invoices; the buyer is the customer billed.
- **Time**: microseconds since 1970-01-01T00:00:00Z, the unit of a room's `t0` (live-rooms §3.1).
A **period** is a span of time from its `start` (included) to its `end` (excluded).
- **Minor unit**: a currency's smallest unit (ISO 4217); an **amount** is an integer of
millionths of one, as settlement §7.3's amounts are.
- **Source**: one record a ledger bills from, with what its own verifier is given (§5).
- **Record identity**: the 32 bytes a record of a kind is named by (§5.2).
- *u64*, *u32*, *u16*, *u8*: little-endian unsigned integers of that many bits. A **digest** is
SHA-256 (FIPS 180-4), 32 bytes; **zero** of a key or digest field is 32 zero bytes.
- **Class**, **tag**, **unmetered marker**: an acquisition class of energy-measurement §2.7, its
tag byte (2 `ModelBased`, 3 `Estimator`, 4 `OnChipCounter`, 5 `CalibratedInstrument`), and a
figure of 0 with no class.
## 3. One tariff across rooms, packages and HTTP delivery
The records a deployment bills from state their units in their own terms. A room-session receipt
states microseconds and events (live-metering §4.4), and may state a `tariff` of its own, which
prices the session's grants against their funds ceilings (§4.6 there). A settlement statement
states acknowledged bytes by object class, whatever carried them: a relay over MOQT, or a cache
over HTTP whose deliveries were acknowledged as settlement §9.1 maps them; it may state a tariff
too, which prices its own lines (§7.3 there). A package receipt states entries and bytes
(packaging §9) and no tariff. An ad decision's receipt states impressions (live-ads §8.2) and
no tariff of the seller's; so does a stream another party stitched, whose stitcher signs a
pod-input record, `WSI1`, of the ads it chose and whose players sign a claim, `WSC1`, of each ad
presented (stitched-presentation §4, §5).
A tariff of this extension prices every one of those units in one currency, so one invoice bills
a buyer's rooms, deliveries, packages and ads together. Three rules keep every figure in it
consistent with what the records say:
1. **A record that states a tariff states this one.** A room-session receipt of a source either
states no tariff (currency zero) or the tariff's currency and its room prices, in
live-metering §4.1's order: the thirteen, and the price of bytes out, which a receipt of
version 1 and a tariff of versions 1 to 3 do not state and read as 0; a settlement statement either states no tariff or the tariff's
currency and its four delivery prices, in settlement §7.1's order. A record under another
tariff is refused `tariff_conflict` (§7.2 step 5): the funds its grants were held to, or the
amount its party stated, were computed at other prices, and an invoice at these prices would
contradict it.
2. **One rounding, the same everywhere.** Every amount is a quantity times a price, divided by
the unit's divisor and rounded down once per invoice line (§6.4), as settlement §7.3 rounds a
statement's line. A statement's own amount rounds once per payer; the invoice rounds once per
party and class, so the two can differ by less than one millionth of a minor unit per payer
line, and the delivery report (§8.2) states both.
3. **Units are never converted.** A room is billed in its own units (in version 4, its bytes
out too), a delivery in bytes, a package in packages, entries and stored bytes (in version
4, in byte-days held), an ad in impressions, a job in jobs or in the subpixels its output's
receipt states (§4.5), and in version 4 in the work units of the packages it made. No line turns one unit
into another, and no line is priced from joules: the energy of an invoice is stated beside
its lines (§6.6), per class, and is not an amount.
So the tariff is the one place prices live: a room receipt's funds verdicts, a statement's
amount and an invoice's lines all follow from it, and a buyer who holds the tariff and the
records recomputes all three.
## 4. The tariff
### 4.1 Layout
| field | size | meaning |
|---|---|---|
| `magic` | 4 | `WLT1` (ASCII) |
| `version` | 1 | 1; 2 for a tariff that prices jobs (§4.5); 3 for one that prices them per operation and bills their work (§4.6, §6.9); 4 for one that also prices bytes out, a package's work and its storage by byte-time (§4.7); 5 for one that holds a decode to its frames (§4.8); 6 for one that also prices a job per endpoint and by a third party's usage, and bills an upscale and a tone mapping (§4.9) |
| `seller` | 32 | the seller's key, which signs the tariff and every invoice under it |
| `buyer` | 32 | the buyer's key, which accepts the tariff by signing it |
| `start`, `end` | 8 each | u64: the period the tariff is good for |
| `currency` | 3 | three capitals `A`–`Z` (ISO 4217) |
| `room` | 52, in version 4 56 | thirteen u32: millionths of a minor unit per second of `publisher_us`, of `subscriber_us` for each layer class 0 to 8, of `composition_us` and of `recorded_us`, and per event sent (live-metering §4.1's order); in version 4 a fourteenth, per 10^9 bytes of `bytes_out` (§4.7) |
| `delivery` | 32 | four u64: millionths of a minor unit per 10^9 acknowledged bytes of each object class, in class order (settlement §7.1) |
| `package` | 24, in version 4 8 | three u64: millionths of a minor unit per package receipt, per entry, and per 10^9 stored bytes; in version 4 one, per 10^9 byte-days held (§4.7) |
| `ads` | 16 | two u64: millionths of a minor unit per presented impression, and per billable impression |
| `job` | 2 + Σ (18 + *c*), in versions 3 and 4 2 + Σ (19 + *c* + *o*), in version 6 2 + Σ (21 + *c* + *o* + *e* + *u*) | versions 2 to 4, absent in version 1: a u16 count *j*, then each job price (§4.5, §4.6, §4.9): `cap_len` (u8), `capability` (`cap_len` bytes), in versions 3 and 4 `op_len` (u8) and `operation` (`op_len` bytes), in version 6 `end_len` (u8) and `endpoint` (`end_len` bytes), then `unit` (u8), in version 6 `use_len` (u8) and `usage` (`use_len` bytes), then `divisor` (u64) and `price` (u64) |
| `scope` | 2 + 33 × *n* | a u16 count *n*, then each entry: `kind` (u8) and `id` (32), in strictly ascending order of (`kind`, `id`) |
| `seller_sig` | 64 | §4.2 |
| `buyer_sig` | 64 | §4.2 |
### 4.2 Digest and signatures
The **digest** of a tariff is SHA-256 over its bytes before `seller_sig`, the **body**. Its
`seller_sig` is Ed25519 by `seller` over the 18 bytes `wai:ledger-tariff` (ASCII) and 0x01,
followed by the body; its `buyer_sig` is Ed25519 by `buyer` over the 18 bytes `wai:ledger-accept`
(ASCII) and 0x01, followed by the body. Both are verified strictly, as [SPEC](../SPEC.md) §7
*Signatures* requires: a key that fails step 1 or 2 is `bad_key`, and a signature that fails a
later step is `bad_signature`. The buyer's signature is what binds the buyer to the prices: a
tariff the buyer did not sign bills nobody.
### 4.3 Reading and rules
A reader reads exactly the bytes it is given as one tariff, in the order of §4.1, and refuses at
the first of these that holds: fewer than 4 bytes, `truncated`; a magic other than `WLT1`,
`malformed`; no fifth byte, `truncated`; a version other than 1 to 6, `unknown_version`; the
bytes end before a field the layout requires, `truncated`; bytes after `buyer_sig`, `malformed`.
It then refuses as `tariff_invalid` a tariff that breaks any of these:
- `start` is below `end`; `currency` is three capitals `A`–`Z`;
- `seller` and `buyer` are not zero, and not the same key;
- `scope` has at least one entry, in strictly ascending order of (`kind`, `id`), each `kind` 1 to
4 (1 to 5 in versions 2 to 4) and each `id` not zero;
- in version 2, `job` has at most 255 prices, each as §4.5 requires, in strictly ascending
order of (`capability`'s bytes, `unit`); in versions 3 and 4, at most 255, each as §4.6
requires, in strictly ascending order of (`capability`'s bytes, `operation`'s bytes, `unit`);
each `unit` 1 or 2, and in version 4 1 to 3;
- in version 6, at most 255, each as §4.9 requires, in strictly ascending order of
(`capability`'s bytes, `operation`'s bytes, `endpoint`'s bytes, `unit`, `usage`'s bytes), each
`unit` 1 to 4; and no price of any endpoint beside a price of one for the same `capability`,
`operation`, `unit` and `usage` (§4.9).
Then the seller's signature, then the buyer's: `bad_key`, `bad_signature`.
### 4.4 Scope
A scope entry names whose records the tariff bills:
| kind | `id` | a source is in scope when |
|---|---|---|
| 1 | a room id | a room-session receipt's `room` is the `id` |
| 2 | a party's key | a settlement statement's `party` is the `id` |
| 3 | a packager's key | a package receipt's `signer_pubkey` is the `id` |
| 4 | an ad decider's or a stitcher's key | every receipt of an ads source has `signer_pubkey` the `id` (live-ads §3.4); a stitched source's records are accepted only when their `signer` is such an `id`: the verifier's **signers** (stitched-presentation §7.1) are the scope's entries of kind 4 |
| 5 | a router's key (versions 2 to 4) | a job source's route record has `signer` the `id` (route-record §5.1) |
The scope is what the buyer agreed to pay for: a record signed by a party the tariff does not name
is refused `outside_scope` (§7.2 step 5), whoever submits it.
### 4.5 Job prices (version 2)
A tariff of version 2 prices the media jobs a router routed and a hop served. A **job price**
is a `capability` (route-record §2: 1 to 128 bytes, lowercase ASCII letters, digits, `_` and
`.`, beginning `wai.`), a `unit`, a `divisor` of at least 1 and a `price`: millionths of a
minor unit per `divisor` units of the unit, for a job whose serving hop served that capability.
The units are the records' own figures, never converted:
| `unit` | name | a job's quantity |
|---|---|---|
| 1 | `jobs` | 1 |
| 2 | `subpixels` | the sum of its output receipts' `work_pixel_ops` (integer-payloads §15.1): the subpixels its output decodes to |
| 3 | `work_units` (version 4) | the sum of its output packages' `work_units` (packaging §9.1, §4.7) |
| 4 | `usage` (version 6) | what its billed hop's third-party record states the provider bills, in the price's own unit (§4.9) |
A job the tariff has no price for, by its serving capability, is billed nothing, and a buyer
pays for a capability only at a price it signed. A package job stays a package source (§5.1),
billed by §4.1's package prices.
### 4.6 Job prices per operation (version 3)
A tariff of version 3 prices jobs as §4.5 does, and each job price also names an `operation`:
an operation of WAI's catalogue in route-record §2's form (two words joined by `.`, each a
lowercase ASCII letter and then lowercase letters and digits: `media.encode`, `media.decode`),
of at most 128 bytes. A job is priced by its serving capability **and** its operation, which is
its route record's `operation`, the one its request named (route-record §5.1). The reason is the
work: within one capability, an encode (a motion search, then a codestream written) and a
decode (a reconstruction) spend different energy and compute for the same output units, so one
price per capability overprices one of them or underprices the other, and a buyer pays each at
the price it signed for it. A job the tariff has no price for, by its serving capability and its
operation, is billed nothing, as a job whose capability a tariff does not price is (§4.5): a
price for one operation never bills another.
Version 3 is a version of its own, not a change to version 2: a reader of version 2 cannot read
a job price that names an operation, and the buyer of a version-2 tariff signed version 2's
rules. Version 3 also bills a job's work rather than its output's identity (§5.2, §5.3, §6.9).
A tariff of version 2, its invoices and its sources read, verify and bill exactly as before.
### 4.7 Bytes, work and storage (version 4)
A tariff of version 4 prices jobs as version 3 does, and prices three more things by what
spending on them scales with:
1. **A room's bytes out.** Its fourteenth room price is per 10^9 bytes of a room-session
receipt's `bytes_out`, the payload bytes the hops sent the session's sessions, summed from
their ledgers (live-metering §4.4, a receipt of version 2). A room's time units price how
long its sessions ran; what delivering them costs scales with the bytes carried too, and a
viewer of a high layer and one of a low layer subscribe for the same seconds and are sent
different bytes. The receipt states the price in its own tariff and counts it in each
grant's funds (live-metering §4.6), so a receipt that states a tariff states this one's
fourteen prices (§3). A receipt of version 1 states no bytes, and bills none.
2. **A package's work, per run.** Packaging is deterministic: the same source packed again under
the same policy gives the same package, under the same receipt, from a run that did the work
again with new energy (a customer asks for the work to be done and measured again). So a
package is billed as a job is (§6.9): the **run** that made it is the work, and its receipt is
the product, which each run that made it cites. Billing once per receipt refused a package
made again, though the work was done again. A run is a job of the catalogue's operation
`package.build`, routed and receipted as any job is (route-record §6): a `job` source whose
output is the package (§5.1), billed at the tariff's job prices for its serving capability
and `package.build`, per job and per **work unit**: the package receipt's `work_units`, the
work its figure is expressed against (packaging §9.1, jwp-receipts), one per entry, each a
base or a refinement the packer coded and wrote, so a price per unit bills what the run
spent in proportion to its size. The chain bills each run's route record and hop receipts
once, and a run replayed under a new route record is refused (`record_rebilled`,
`record_duplicate`), as any job's; the package receipt (kind 5) is not billed once, and is
cited by each run's line. The run's energy is its hop's figure (§6.6), and the receipt's own
figure, the same work's, is not stated again. A `package` source is the product held: it
bills its storage alone (below), never its work, which only a run states.
3. **A package's storage, by byte-time.** Its one package price is per 10^9 **byte-days**:
what keeping bytes costs scales with how many are kept and for how long, which a price per
stored byte once per receipt ignores. A package whose receipt the previous invoice of the
chain lists is **held** through the invoice's whole period: it was written by the end of
that invoice's period, at most this one's `start`. The invoice bills it ⌊Σ `stored_bytes` ×
(`end` − `start`) / 86 400 000 000⌋ byte-days over its packager's held packages, the
period in microseconds and rounded down once per line, and lists its receipt again. The
invoice that first lists a package bills no storage for it: no record shows when within that
period it was written, and a buyer pays only for time a record shows the bytes were kept. The period is read from the chain itself, its invoices'
`start` and `end`, which the seller signs and the buyer holds (§6.9), and the bytes from
the package receipt.
A version-3 reader cannot read a tariff of version 4 (its room prices are one longer and its
package prices two shorter, and a job price may be of unit 3) or an invoice of version 4 (§6.1:
it states a sixth unmetered count), so version 4 is a version of its own. A tariff of versions
1 to 3, its invoices and its sources read, verify and bill exactly as before.
### 4.8 A decode's output (version 5)
A tariff of version 5 is version 4's in its layout (§4.1), its rules (§4.3), its scope and its
prices (§4.4 to §4.7), and an invoice of version 5 is version 4's (§6.1); each bills what
version 4 bills. Where this text says version 4, or a range of versions ending at 4, version 5
is included, except as this section says.
A decode (a job whose route record's `operation` is `media.decode`) delivers its
**reconstruction**, not a codestream: the codestream is what it read. Version 4 gives every
`int_motion` output as its receipt and its `codestream` and holds the codestream to what the hop
delivered (§5.1), which is true of an encode and false of a decode, whose hop would have to state
an object it never delivered and commit its receipt to it (route-record §5.2, §6). Under a tariff
of version 5 a decode's output is held to what it delivered:
1. Each output of a decode's job source is an object of exactly `receipt` (the text of a
`wai.video.int_motion` decode receipt's JSON form, integer-payloads §15.2), `codestream` (the
`WIV1` payload the decode read, hex) and `frames` (one animated PNG holding its frames, hex).
The receipt is read by integer-payloads §15.2 and re-derived from the codestream (§15.1 there):
the codestream is the decode's input, and the receipt is the evidence of what it decodes to.
2. The animation is read whole first: its chunks as packaging §5 reads a still's (every chunk's
type four letters and its CRC checked; `IHDR` first, of 8-bit RGB without interlace; an
optional `PLTE` before the image data; the `IDAT`s consecutive; ancillary chunks read past;
any other critical chunk refused; `IEND` last, empty, and nothing after it), and the chunks
of PNG's third edition that make it an animation:
- one `acTL` of 8 bytes, before any `fcTL` and the image data, counting at least one frame,
and as many `fcTL`s as it counts;
- each `fcTL` of 26 bytes, of the whole picture (its width and height `IHDR`'s, its offsets
0), its dispose operation 0 to 2 and its blend operation 0, any delay;
- the first `fcTL` before the `IDAT`s, so the default image is the first frame, and every
other after them, followed by at least one `fdAT` before the next `fcTL` or `IEND`; no
`IDAT` but the first frame's, and no `fdAT` of it;
- the sequence numbers of the `fcTL`s and `fdAT`s 0, 1, 2, … in their order, and every
`fdAT` at least 4 bytes long.
It is of the receipt's `w` × `h` and holds as many frames as the receipt has. Then each frame,
in order: its data (the `IDAT`s' for the first frame, its `fdAT`s' after their sequence
numbers for each other, concatenated) is read as packaging §5 reads a still's image data, and
the frame hash of its samples at its index (integer-payloads §15.1: BLAKE3 over
`wai:video-rframe`, 0x01, the index as a u64 and the RGB8 bytes) is the receipt's frame at
that index. Otherwise the source is refused `output_invalid`: at `outputs`, the output's
index and `frames` where the animation breaks a rule above or differs in size or count; at
`outputs`, the output's index, `frames` and the frame's index where that frame's data is not
the picture's rows or its samples are not the receipt's; and at `outputs` and the output's
index when the receipt or the codestream does not hold.
3. The objects the served attempt states it delivered (route-record §5.2) are exactly the
decode's animations, by content hash, in the order of its outputs, and nothing else: an
animation not stated at its place is refused `output_not_delivered` at `outputs`, the
output's index and `frames`, and an object stated past the last, the codestream among them,
at `outputs`. Under a route record of version 3 an animation is at its place when its proof
holds it there (§5.1, its `index` its place), and there is none past the last when the
attempt's `count` is the number of animations.
A decode delivers its frames as one object because a clip's frames are many and repeat: a route
record states each of a hop's outputs once, and at most 1024 of them (route-record §5.2), while
a clip runs to thousands of frames and a still scene decodes to the same frame again and again.
An animated PNG is lossless, one file every browser plays and any tool splits into stills, and
its default image is its first frame, which packaging §5's reader of a still reads. The frame
hash, not the PNG's bytes, is what the receipt binds, so any writer's animation of the right
samples holds; the reference writer is `png_still::encode_animation`. A frame is the whole
picture and its samples replace the picture's, so they are its data alone: a frame blended over
the one before, or of part of the picture, would be held to samples its data does not hold.
A decode's billed subpixels are its receipts' `work_pixel_ops` (§6.3), as in version 4: the
receipt states what the frames hold. An encode, and every other job, is held as in version 4.
A version-4 reader would read a decode's `frames` as an unknown member and refuse the source, and
a version-4 verdict on a decode must not change under it, so version 5 is a version of its own.
A tariff of versions 1 to 4, its invoices and its sources read, verify and bill exactly as
before.
### 4.9 An endpoint, a third party's usage, and what re-derives an output (version 6)
A tariff of version 6 is version 5's in its layout but for its job prices (§4.1), in its rules
but for theirs (§4.3), and in its scope and prices (§4.4 to §4.8), and an invoice of version 6 is
version 5's (§6.1) but for the records it may list; each bills what version 5 bills. Where this
text says version 4 or 5, or a range of versions ending at either, version 6 is included, except
as this section says.
**A job price names an endpoint.** Its `endpoint` is the endpoint of the attempt it prices, a
token of route-record §2's form, or empty, which prices every endpoint. Two providers of one
capability have different list prices, so one price per capability overprices one or underprices
the other, and a buyer pays each at the price it signed for it. A job is priced as §4.6 prices it
and, where a price names an endpoint, by its billed hop's. A price of any endpoint beside a price
of one, for the same capability, operation, unit and usage, would bill one job at both, so a
tariff that has both is `tariff_invalid`: its seller states one price for every endpoint or a
price for each.
**A third party's usage.** A job price of unit 4 names `usage`, a unit of route-record §2's form,
and of any other unit none. A job source may give `third_party`, a list of third-party hop
records' texts, which route-record §7.2 step 9 verifies with the rest of its document, a refusal
there being the source's. Each record contributes itself (§5.2, kind 17). A job's quantity at a
price of usage is the `usage.quantity` of its billed hop's record, when that record's `usage.unit`
is the price's `usage`, and otherwise none:
- a job given no record bills nothing at a price of usage, and bills by its other units;
- a failed hop's record, and a late hop's, state what the provider billed for it, which is what
the router's operator owes the provider, and no line reads it: only the billed hop's usage
reaches a line, as §5.1 holds a failed hop's figures from a line;
- a record's `charge` reaches no line. It is the provider's own figure in its own terms, and a
line adds jobs, so it cannot be summed. The line cites the record, which states it signed, so a
buyer reads the two side by side.
A third party's output has no WAI receipt, and a job it served gives no output receipt for it: its
record names the objects, and the route record holds them to the hop (route-record §6.2).
**An upscale.** A job whose route record's `operation` is `media.upscale` gives each output as an
object of exactly `upscale` (the text of an upscale record, exact-upscale §4.2), `object` (the
input: a `WAI1` of capability `wai.image.png` holding a PNG of 8-bit RGB that packaging §5's reader
reads, hex), `prior` (the `WSR1` it names, hex) and `picture` (the object the hop delivered: a PNG
that reader reads, hex). The record is verified by exact-upscale §7 under `require-proof`, holding
the input, its samples and the prior, and the picture is of the size the record's output is, with
the samples whose digest is its `output`. So what is billed is what the derivation gives: the
record's subpixels are the output's width × height × 3, the record contributes itself by its link
hash (§5.2, kind 18), and the picture is its object (§5.1). No residual is given, so a record that
names one is not proved here. Otherwise the source is refused `output_invalid`: at `outputs`, the
output's index and `object` where the input does not read so; at `outputs`, the output's index and
`picture` where the picture does not read or is not the output; and at `outputs` and the output's
index where the record does not read or is not proved.
**A tone mapping.** A job whose route record's `operation` is `media.tonemap` gives each output as
an object of exactly `tonemap` (the text of a rendition record, exact-tonemap §7.1), `envelope`
(the `WAI2` it read, hex) and `renditions` (each picture's rendition, its `RGB16` bytes, exact-tonemap
§2, in the record's order, hex), and under a route record of version 3 `proofs`, in place of
`proof`: one inclusion proof for each rendition (route-record §6.1). The record is verified by
exact-tonemap §8 under `require-proof`, holding the envelope and no decoded frame, so what is proved
is a still's pictures, and a record of a video's frames is refused. There is one rendition for each
picture, whose digest is the picture's `output`. So the record's subpixels are its pictures × width
× height × 3, and it contributes itself by its link hash (§5.2, kind 19). Otherwise the source is
refused `output_invalid`: at `outputs`, the output's index, `renditions` and the rendition's index
where a rendition is not its picture's; at `outputs`, the output's index and `renditions` where
their number is not the record's; and at `outputs` and the output's index where the record does not
read or is not proved. Each rendition is an object the hop delivered, and none is another output's
(§5.1): otherwise `output_not_delivered`, at `outputs`, the output's index, `renditions` and the
rendition's index. Two pictures of one record may render one picture: the hop states that object
once (route-record §5.2), and both are billed, since the record states and proves both.
A job of either operation gives its outputs in that form alone, and any other form is `malformed`.
A job's `subpixels` (§4.5) sum its output receipts' `work_pixel_ops` and these records' subpixels.
These records are the evidence of what a job delivered, as an output receipt is (§6.9): the
derivations are exact, so a run of the same request delivers the same picture, and a later invoice
may cite a record again for a new hop's work. Each is a record of the signer that made it, and what
its figure rests on is the derivation, which any holder of the source recomputes.
A version-5 reader cannot read a job price that names an endpoint, a job source's `third_party`,
these outputs or kinds 17 to 19, so version 6 is a version of its own. A tariff of versions 1 to 5,
its invoices and its sources read, verify and bill exactly as before.
## 5. Sources
### 5.1 Kinds and their documents
A source is a JSON object (RFC 8259) whose `kind` names its extension, holding exactly what that
extension's verifier is given. Bytes are lowercase hexadecimal strings.
| `kind` | members | verified as |
|---|---|---|
| `room` | `room` (`descriptor`, `grants`, `key_log_head` (a string or `null`), `chains`, a list of lists of records), `receipt`, `hops` (each `record` and `ledger`), `descriptors`, `compositions`, `takes`, `logs` (a string or `null`) | live-metering §5.1, its receipt being `receipt` |
| `delivery` | `statement`, `records` (each `record` and its `items`, one item list per entry), `proofs`, `hops`, `rooms` (each `descriptor` and `grants`) | settlement §8.2 |
| `package` | `receipt` (the text of `package.receipt.json`), `indexes` (the index files in the receipt's component order) | packaging §9, a receipt of version 2 against its indexes |
| `ads` | `descriptor`, `grants`, `key_log`, `chains`, `policy`, `pt0_ms`, `receipts` (form-2 receipts in their JSON carriage), `pods`, `claims`, `creatives`, `issuers` | live-ads §9, with ad-presentation §9 under a policy that requires claims |
| `stitched` | `records` (pod-input records), `claims` (stitched claims), `manifests`, `responses` (VAST and VMAP documents), `receipts` (form-1 receipts in their JSON carriage), `creatives`, `issuers` | stitched-presentation §7, its signers the tariff's (§4.4) |
| `job` (versions 2 to 4) | `request`, `pricebook`, `policy`, `record`, `receipts`, `keys` (a route record's verification document, route-record §7.1, its `receipts` and `keys` not `null`) and `outputs`, a list of objects of exactly `receipt` (the text of a `wai.video.int_motion` decode receipt's JSON form, integer-payloads §15.2) and `codestream` (the `WIV1` payload it describes: the object the hop delivered, named by its content hash; in version 5 a decode's output also has `frames`, the animation of its frames it delivered, and its codestream is what it read, §4.8), or, in version 4, of exactly `package` (the text of a package receipt of version 2, `package.receipt.json`: the object the hop delivered) and `indexes` (its index files in its component order); under a route record of version 3, each output also has `proof`, its object's inclusion proof (route-record §6.1); in version 6, it may also have `third_party`, and an upscale's and a tone mapping's outputs have their own forms (§4.9) | route-record §7, accepted with its hop receipts checked; then each output receipt read by integer-payloads §15.2 and re-derived from its `codestream` (§15.1 there), each package verified against its indexes by packaging §9, or `output_invalid` at `outputs` and its index, an upscale's and a tone mapping's outputs as §4.9 says; then each output held to what its hop delivered, or `output_not_delivered` there |
A source is accepted when its extension's verifier accepts it; for `ads`, when the outcome is
**accept** (live-ads §9.4), a **gap** being `source_gap`. A `stitched` source is always
accepted, since its verification refuses nothing as a whole (stitched-presentation §7.5): a
record it does not accept, of a signer outside the scope among them, bills nothing, and a claim
it does not find presented counts nothing. A source that is not such an object,
whose `kind` is none of these, or whose members are not exactly its kind's, each of its form, is
refused `malformed`. Under a tariff of version 1, a `job` source is not a kind, and is
`malformed`.
A job source's **billed hop** is the attempt of its route record that is billed (route-record
§5.2), if any; its **serving capability** is that attempt's candidate's `capability`, its
**operation** is its route record's `operation`, and its **figure** is the figure of that
attempt's OER/2 receipt: the unmetered marker
(oer-receipts §3.1), or `energy.joules_total` in microjoules, rounded to the nearest and a
half to the even one, with the receipt's `energy.class`. A billed hop's receipt whose figure
has no class, or rounds below 1 µJ or past what a u64 holds, cannot be stated in a row
(§6.6): the source is refused `job_figure_invalid`, at `receipts` and the receipt's index.
Under a tariff of version 4 every attempt's figure is stated (§6.6), so the same holds of each
attempt that is not billed, a failed hop and one that served past the deadline.
**A job's output is the object its hop delivered.** An output's **object** is its
`codestream`, or a package's receipt text, named by its content hash (route-record §2,
jwp-receipts §2.1). A route record
of version 2 states the objects its served attempt delivered, by content hash, and holds that
hop's receipt to them (route-record §5.2, §6). Each output a job source gives is then one of
those objects, and no two of its outputs are one object: otherwise the source is refused
`output_not_delivered`, at `outputs` and the output's index. The reason is the subpixels a
line bills (§6.3): they are read from the output receipts, and an output receipt of an object
the hop did not deliver bills subpixels nobody delivered for this job, as a receipt given
twice bills them twice. So the chain closes: the hop's receipt names its outputs, the record
names the hop's receipt and the outputs, and each output receipt is of one of them. Under a
tariff of version 5 a decode's object is the animation of the frames it delivered, not the
codestream it read (§4.8). Under a tariff of version 4, a record that states no outputs (a
record of version 1, or one no attempt served) holds every output to none, and a job it
routed is billed by the job alone; under a tariff of version 3, a record of version 1's
outputs are read as before.
A route record of version 3 states what its served attempt delivered by its count and root,
not by a list (route-record §3.4, §5.2), so a hop may deliver any number of objects. Each
output of a job source under one gives `proof`, the inclusion proof of its object (route-record
§6.1), and is one of the objects the hop delivered when its proof holds its object's content
hash to the served attempt's `count` and `root`; an output under a record of version 1 or 2
gives none. An output without its proof, or with one under a record of version 1 or 2, or a
proof that is not an object of exactly an integer `index` and a list of content hashes'
texts, `siblings`, is `malformed`; a proof that does not hold is `output_not_delivered`, at
`outputs` and the output's index, as an object a list does not state is; and no two outputs
are one object, as before. The ledger does not hold the count to the objects, which it is not
given: the hop's receipt names the root alone (route-record §6). A count stated larger than
the objects lets the last of an odd level be proved at a second place, and that place is one
object, which no source bills twice; a decode is held to its places and its count (§4.8).
Under a record of version 3 no attempt served, the outputs are held to none.
**Routing figures are never charges.** A route record's candidates' `cost`, `energy`,
`expected_cost` and `expected_ttfb_ms`, and its price book's `price` and `failures`, are the
router's figures for choosing an endpoint (route-record §4.2, §4.4): what a hop costs the
router, and what the failures it measured cost it, per successful job. No line reads them. A
job line bills its billed attempt alone, at its list price, the tariff's job price for its
serving capability and operation, which the buyer signed: a failed attempt, what a provider
billed for it and the expected cost per successful job never reach a line, however the router
ranked, and a line that counts a failed hop as a job, or prices one at the router's figures,
does not recompute.
### 5.2 What a source contributes
Each source contributes **records**, each named by a kind and an identity. This table is the
**one registry of record kinds**: every WAI text that names a record by a kind (in a citation
digest, or in a list of records) takes the kind from here, and a new kind is assigned here, the
next free one. Kinds 10 to 13 are defined by the texts that cite them and are no ledger source's;
the others are the ledger's own:
| record kind | record | identity | contributed by |
|---|---|---|---|
| 1 | a room-session receipt, `WMR1` | its digest (live-metering §4.2) | a `room` source: its receipt |
| 2 | a per-hop record, `WMH1` | its digest (live-metering §3.5) | a `room` source: each record its receipt names; a `delivery` source: each record its statement names in `hops` |
| 3 | a settlement statement, `WSS1` | its digest (settlement §7.4) | a `delivery` source: its statement |
| 4 | an acknowledgement or declaration, `WDA1` | its digest (settlement §3.5) | a `delivery` source: each record its statement names in `records` |
| 5 | a package receipt | its `receipt_hash` (packaging §9) | a `package` source; a `job` source (version 4): each output package |
| 6 | a `wai.video.ssai` receipt of the break form | its identity (live-ads §8.4) | an `ads` source: each receipt |
| 7 | a presentation claim, `WPC1` | its digest (ad-presentation §5.3) | an `ads` source: each claim that reads |
| 8 | a pod-input record, `WSI1` | its digest (stitched-presentation §4.4) | a `stitched` source: each record it accepts |
| 9 | a stitched claim, `WSC1` | its digest (stitched-presentation §5.3) | a `stitched` source: each claim that reads |
| 10 | a run of a channel's program-as-run | its identity ([revenue-share](revenue-share.md) §7.4) | no source: cited by revenue-share §7.4 and opportunity-report §7.3 |
| 11 | an invoice, `WLI1` | its digest (§6.7, as [revenue-share](revenue-share.md) §7.4 cites it) | no source: cited by revenue-share §7.4 |
| 12 | a cue event | its event digest ([opportunity-report](opportunity-report.md) §7.3, live-events §3.2) | no source: cited by opportunity-report §7.3 |
| 13 | a room policy | its digest ([opportunity-report](opportunity-report.md) §7.3, live-ads §3.2) | no source: cited by opportunity-report §7.3 |
| 14 | an OER/2 receipt | the 32 bytes its `receipt_id` names (oer-receipts §5) | a `job` source (versions 2 to 4): the receipt of each attempt of its record |
| 15 | a `wai.video.int_motion` decode receipt | its receipt hash (integer-payloads §15.1) | a `job` source (versions 2 to 4): each output receipt |
| 16 | a route record | its digest (route-record §5.3) | a `job` source (versions 2 to 4): its record |
| 17 | a third-party hop record | its digest (route-record §6.2) | a `job` source (version 6): each record it gives |
| 18 | an upscale record | its link hash (exact-upscale §2) | a `job` source (version 6): each upscale's record (§4.9) |
| 19 | a rendition record | its link hash (exact-tonemap §2) | a `job` source (version 6): each tone mapping's record (§4.9) |
Kinds 20 to 127 are unassigned. Kinds **128 to 255 are reserved for private and provisional
use**: an implementation's own records, such as a hosted service's provisional records carried
beside WAI's until WAI defines them, may name records by them, and no kind WAI assigns later will
ever be one of them. A WAI record never states one, and a WAI verifier refuses one wherever a WAI
record states a kind, `private_kind`: in this extension, an invoice's `records` (§6.8). No citation
digest WAI defines covers one.
A source's **head** is its receipt, statement or package receipt; an `ads` source's heads are its
receipts, a `stitched` source's its accepted records, and a `job` source's its route record. A head of one source that another source of the invoice also contributes is refused
`record_duplicate`; a per-hop record, which a room receipt and a statement can both name, is
contributed once. An OER/2 receipt a `job` source contributes that an earlier `job` source of the
invoice also contributes is refused `record_duplicate` too: one hop serves one job, so no hop is
billed for two. Under a tariff of version 2, so is an output receipt: one output is one job's.
Under a tariff of version 3 or 4 it is not: an output receipt is the evidence of what a job
delivered, not the work, and the same request run again on a deterministic codec delivers the
same object, of the same content hash, under the same receipt from a new hop that did the work
again (§6.9). Two lines of one invoice that cite one output receipt therefore cite different hop
receipts, whichever lines they are.
### 5.3 Placement in time
A source is **in** a period when:
- `room`: the receipt's session, placed by its room's `t0`, lies within it: `t0` + `start` ≥ the
period's `start` and `t0` + `end` ≤ its `end`;
- `delivery`: the statement's period lies within it;
- `ads`: the `t` of the break each receipt names, placed by the room's `t0`, is within it;
- `package` and `stitched`: always: a package receipt and a pod-input record state no time, and
the chain (§6.9) bills each once; under a tariff of version 4, a package's work once and its
storage for the periods it is held through (§4.7);
- `job`: its route record's `accepted` is within it.
Two room receipts of one room whose sessions overlap, two statements of one party whose periods
overlap, two receipts of the invoice's ads sources naming one break and one session, and, under
a tariff of version 2, two route records of one router naming one `job`, bill one thing twice:
the later is refused `record_overlap`. Under a tariff of version 3 or 4, two route records of one
job are two runs of it, each billed from its own hop, and no hop twice (§5.2): a job's id names what
was asked, not the work, and a request run again is worked again.
## 6. The invoice
### 6.1 Layout
| field | size | meaning |
|---|---|---|
| `magic` | 4 | `WLI1` (ASCII) |
| `version` | 1 | its tariff's version, 1 to 5 |
| `seller`, `buyer` | 32 each | the tariff's |
| `tariff` | 32 | the tariff's digest |
| `prev` | 32 | the digest of the invoice before it in its chain (§6.9); zero for the first |
| `start`, `end` | 8 each | u64: the period it bills |
| `currency` | 3 | the tariff's |
| `records` | 4 + 33 × *r* | a u32 count, then each record (§5.2): `kind` (u8) and `id` (32), in strictly ascending order of (`kind`, `id`) |
| `lines` | 2 + 94 × *l* | a u16 count, then each line (§6.3), in strictly ascending order of (`source_kind`, `source`, `unit`) |
| `energy` | 1 + 18 × *e* | a u8 count, then each row (§6.6): `scope` (u8), `tag` (u8), `figures` (u32), `joules` (u64, µJ), `max_ppm` (u32), in strictly ascending order of (`scope`, `tag`) |
| `unmetered` | 16, 20 or 24 | four u32: the unmetered figures of scopes 1 to 4 (§6.6); in versions 2 and 3, five, scopes 1 to 5; in version 4, six, scopes 1 to 6 |
| `amount` | 8 | u64: the sum of the lines' amounts |
| `sig` | 64 | §6.7 |
### 6.2 Records
`records` lists every record the sources contribute (§5.2), each once. It is what the invoice was
computed from, and what the next invoice of the chain may not bill again.
### 6.3 Lines
A line is 94 bytes:
| field | size | meaning |
|---|---|---|
| `source_kind` | 1 | 1 room, 2 delivery, 3 package, 4 ads, 5 job (versions 2 to 4) |
| `source` | 32 | the room id, the delivering party, the packager, the ad decider or the router |
| `unit` | 1 | the unit (below) |
| `quantity` | 8 | u64: the quantity of the unit the records state |
| `price` | 8 | u64: the tariff's price for the unit |
| `amount` | 8 | u64: §6.4 |
| `cited` | 4 | u32: the records the line cites (§6.5) |
| `cites` | 32 | their **citation digest** (§6.5) |
The units, the quantity each sums over the sources of its `source`, and the price and divisor of
each:
| `source_kind` | `unit` | name | quantity | price | divisor |
|---|---|---|---|---|---|
| 1 | 1 | `publisher_us` | the receipts' `publisher_us` | `room` 0 | 10^6 |
| 1 | 2–10 | `subscriber_us_0`–`subscriber_us_8` | their `subscriber_us` of layer class `unit` − 2 | `room` 1–9 | 10^6 |
| 1 | 11 | `composition_us` | their `composition_us` | `room` 10 | 10^6 |
| 1 | 12 | `recorded_us` | their `recorded_us` | `room` 11 | 10^6 |
| 1 | 13 | `events_sent` | their `events_sent` | `room` 12 | 1 |
| 1 | 14 | `bytes_out` (version 4) | their `bytes_out`, of receipts of version 2 (§4.7) | `room` 13 | 10^9 |
| 2 | 1–4 | `live`, `fetch`, `fill`, `ancillary` | the bytes of the statement's delivery lines of that class that are not free (settlement §7.2) | `delivery` `unit` − 1 | 10^9 |
| 3 | 1 | `packages` | the number of package receipts | `package` 0 | 1 |
| 3 | 2 | `entries` | their `total_entries` | `package` 1 | 1 |
| 3 | 3 | `stored_bytes` | their `stored_bytes` | `package` 2 | 10^9 |
| 3 | 3 | `byte_days` (version 4, its only package unit) | ⌊Σ `stored_bytes` × (`end` − `start`) / 86 400 000 000⌋ over the package receipts the previous invoice lists, which are held (§4.7) | `package` 0 of version 4 | 10^9 |
| 4 | 1 | `presented_impressions` | under a policy that requires claims, the impressions that count (ad-presentation §9.2); of a stitched source, the presentations of its accepted records' ads (stitched-presentation §7.4), one per session | `ads` 0 | 1 |
| 4 | 2 | `billable_impressions` | under a policy that does not, the billable impressions (quartile at least 1) | `ads` 1 | 1 |
| 5 | *k* | the *k*-th job price's unit | over the router's job sources with a billed hop whose serving capability is that price's `capability` (and, in versions 3 and 4, whose operation is its `operation`, and, in version 6, whose endpoint is its `endpoint`, when it names one): one per job (`jobs`), each job's output receipts' `work_pixel_ops` (`subpixels`), in version 6 with its upscale and rendition records' subpixels, or, in version 4, its output packages' `work_units` (`work_units`), or, in version 6, its billed hop's record's usage in the price's unit (`usage`, §4.9) | the *k*-th job price's `price` | its `divisor` |
In version 4 a package source bills only its `byte_days`, and its work is its run's job line.
There is one line for each source and unit whose
quantity is above 0, a price of 0 included, and none for a quantity of 0. A delivery line's bytes are those its party's downstream parties signed
for (settlement §3.7): a declared byte and a free retrieval are never billed, and the delivery
report states both (§8.2). An ad line counts impressions under one rule or the other, never both:
a policy that requires claims bills only what the players' own claims show presented, and one that
does not bills on the decider's beacons, under a unit of its own.
### 6.4 Amounts
A line's **amount** is ⌊`quantity` × `price` / divisor⌋, computed in integers wide enough not to
overflow, and the invoice's `amount` is the sum of its lines' amounts. A line amount or a sum past
what a u64 holds cannot be stated, and a ledger refuses to compute such an invoice.
### 6.5 Citations
Each line cites the records it came from:
- a room line: the receipts of its room whose figure of the unit is above 0;
- a delivery line: the statements of its party with a delivery line of the class that is not free,
and each acknowledgement (mode 1) they name with an entry of the class that is not free (a
retrieval class with a zero `grant` is free);
- a package line: the package receipts of its packager whose figure of the unit is above 0; in
version 4, the held receipts its `byte_days` count;
- an ad line: the receipts of its decider with at least one impression counted under the unit, and,
for `presented_impressions`, each counted impression's own claim; of a stitched source, its
stitcher's accepted records, and each presented own claim of their ads;
- a job line: for each job it counts, its route record and its billed hop's OER/2 receipt, and,
for `subpixels`, each of its output receipts whose `work_pixel_ops` is above 0. In versions 3
and 4, an output receipt that two jobs of the line delivered is cited once and its
`work_pixel_ops` counted for each (§6.9); in version 4, for `work_units`, each of its output
packages whose `work_units` is above 0, a package that two runs of the line made cited once
and counted for each; in version 6, for `subpixels`, each of its upscale and rendition records,
counted as an output receipt is, and for `usage`, its billed hop's third-party record.
`cited` is their number, and `cites` is SHA-256 over the 17 bytes `wai:ledger-cites` (ASCII) and
0x01, followed by each cited record's `kind` and identity, in ascending order of (`kind`,
identity). Every cited record is in `records`. A buyer recomputes a line from the records it
cites alone, and shows one line to an auditor with its records and nothing else. One line is
the exception: a job line of version 3 or 4 two of whose jobs delivered one output receipt names each job
and that output once, and how many of its jobs delivered the output is stated by their sources,
each of which pairs its job with its outputs (§5.1).
### 6.6 Energy
Each record that states an energy figure states it once, in one **scope**:
| scope | figures |
|---|---|
| 1 `room_hops` | the per-hop records named by the room receipts (each restated by its receipt, live-metering §4.5) |
| 2 `delivery_hops` | the per-hop records named by the statements and by no room receipt of the invoice |
| 3 `packaging` | the package receipts' `joules_micro` and class; in version 4 none: a package's figure is its run's, which the run's hop states (§4.7) |
| 4 `ad_decisions` | the ad receipts' `joules_micro` and class; a pod-input record states no figure |
| 5 `jobs` (versions 2 to 4) | each job source's billed hop's figure (§5.1): the hop that served the job, in the class its OER/2 receipt states; a job with no billed hop states none |
| 6 `unbilled_hops` (version 4) | each job source's attempts that are not billed (route-record §5.2): a failed hop and one that served past the deadline, each its OER/2 receipt's figure (§5.1) |
A failed hop spent energy and delivered nothing, and a hop that served late spent it and
delivered too late: neither is billed, and before version 4 neither appeared in any row, so an
invoice's energy for its jobs was less than their hops spent. Version 4 states them in a scope
of their own, beside the billed hops' and never summed with them: the jobs' energy is scope 5
and scope 6 together, per class, and what the buyer was billed for is scope 5 alone. No line
reads scope 6.
For each scope, `unmetered` counts its figures that are the unmetered marker, which are in no row
and **MUST NOT** be presented as zero joules. For each scope and each tag among its metered
figures there is one **row**: `figures`, their number; `joules`, their sum; and `max_ppm`, the
largest `relative_ppm` among them for tags 4 and 5, which carry a declared uncertainty, and 0 for
tags 2 and 3, which do not. When each figure's error is within its declared relative uncertainty,
the sum's is within `max_ppm` of the sum. A row never sums figures of two classes, and no row sums
two scopes: a relay's counter reading, a packager's model and a decider's estimate are three rows,
since a sum of a model and a counter reading is neither. A row whose `tag` is 0 is refused
`classes_summed`, and so is a row whose `joules` is the sum of the figures of its class and of at
least one other class of its scope, while not the sum of its own (live-metering §4.5's rule). A
scope's energy is **whole** when its `unmetered` is 0 and it has a row, **partial** when it has an
unmetered figure, and **none** when it has no figure; a partial total **MUST NOT** be presented as
the scope's energy.
### 6.7 Digest and signature
The **digest** of an invoice is SHA-256 over its bytes before `sig`. Its signature is Ed25519 by
`seller` over the 19 bytes `wai:ledger-invoice` (ASCII) and 0x01, followed by the bytes the digest
covers, verified strictly (SPEC §7 *Signatures*).
### 6.8 Reading and rules
A reader reads exactly the bytes it is given as one invoice, as §4.3 reads a tariff with `WLI1`
for `WLT1` (`truncated`, `malformed`, `unknown_version`), and refuses as `classes_summed` an
invoice with a row of tag 0. It refuses as `private_kind` an invoice with a record of a kind 128 to 255 (§5.2), and as
`invoice_invalid` one that breaks any of these:
- `start` is below `end`; `currency` is three capitals `A`–`Z`;
- each list is in the strictly ascending order §6.1 gives it; each record's `kind` is 1 to 9 (in
versions 2 to 4, also 14 to 16: kinds 10 to 13 are no ledger source's, §5.2; in version 6, also
17 to 19);
- each line's `source_kind` is 1 to 4 (1 to 5 in versions 2 to 4), its `unit` a unit of its kind
in its version (§6.3; for kind 5, at least 1), its `quantity` and `cited` above 0;
- each row's `scope` is 1 to 4 (1 to 5 in versions 2 and 3, 1 to 6 in version 4), its `tag` 2 to 5
and its `figures` above 0.
Then the signature (§6.7): `bad_key`, `bad_signature`.
### 6.9 Chains
The invoices of one tariff form a **chain**: each names in `prev` the invoice before it, the first
names zero, and their periods do not overlap. A ledger keeps one chain per tariff. No record is in
the `records` of two invoices of a chain, but, under a tariff of version 3 or 4, an output receipt
(kind 15), under one of version 6 an upscale record and a rendition record (kinds 18 and 19, §4.9),
and under one of version 4 a package receipt (kind 5), which each run that made it
cites and each invoice whose period it is held through lists again to bill its storage (§4.7): a record billed once is billed never again, whatever period a later invoice states, and
a package receipt, which states no time, is billed in the invoice that first holds it.
What a chain bills once for a job is its **work**, and its output's receipt is the **evidence**.
A run of a job is a hop that did it: new energy, and a new OER/2 receipt and route record, new
on every run. An output receipt is not the work: the codecs are deterministic, so the same request
run again delivers the same objects, of the same content hashes, under the same receipt. Under a
tariff of version 3 or 4, therefore:
- the records a chain bills once for a job are its route record and its hops' OER/2 receipts,
the billed hop's among them, and in version 6 its hops' third-party records, each of one hop;
- an output receipt is cited, and carried in its lines' citation digests, but is not billed once:
a later invoice may list it again for another job's hop;
- a re-run whose route record and hop receipt are new is billed, in the invoice that holds it;
- a source with a hop receipt or a route record the previous invoice holds is refused
`record_rebilled`, and one with a hop receipt an earlier source of the invoice gives is refused
`record_duplicate` (§5.2): a replay of either is never billed;
- two lines of one invoice may cite one output receipt only under different hop receipts.
Under a tariff of version 2, an output receipt is billed once as well, and a re-run of a job
whose output an invoice of the chain holds is refused, as that tariff's buyer signed it. A
package keeps its rule in versions 1 to 3: it is billed as a delivered product, once per package
receipt, which is a rule of its own and not a job's. In version 4 it has a job's shape: the run
that made it is the work, billed once per run, and its receipt the product, cited by each run;
its storage is billed for each period it is held (§4.7).
## 7. Verification
### 7.1 Inputs
A verifier is given the **tariff**, the **invoice**, the invoice its `prev` names (the
**previous**, or none), and the **sources**, a list. The ledger's verification document holds them
as one JSON object: `tariff` and `invoice` (hexadecimal), `previous` (hexadecimal or `null`) and
`sources` (§5.1).
### 7.2 Steps
It runs these steps and stops at the first refusal, which it reports with the reason and where:
1. **The tariff** (§4.3), at `tariff`, or `tariff` and `seller` or `buyer` for a signature:
`truncated`, `malformed`, `unknown_version`, `tariff_invalid`, `bad_key`, `bad_signature`.
2. **The invoice** (§6.8), at `invoice`: `truncated`, `malformed`, `unknown_version`,
`classes_summed`, `private_kind`, `invoice_invalid`, `bad_key`, `bad_signature`.
3. **The binding**, at `invoice`: its `tariff` is the tariff's digest, and its `version`,
`seller`, `buyer` and `currency` the tariff's: otherwise `tariff_mismatch`; its period lies within the tariff's:
otherwise `outside_tariff`.
4. **The previous**, at `previous`, when `prev` is not zero: an invoice whose digest is `prev` is
given (`record_missing`); it reads and its signature verifies (§6.8, with those reasons); its
`tariff`, `seller` and `buyer` are the invoice's (`tariff_mismatch`); and its `end` is at most
the invoice's `start` (`invoice_overlap`).
5. **Each source**, in order, at `source` and its index: it is verified as §5.1 says, a refusal
being that extension's reason with that extension's place after the index (an `ads` gap is
`source_gap`, and a source that does not read `malformed`); then it is in the scope (§4.4, `outside_scope`); a room receipt or a statement
that states a tariff states this one (§3, `tariff_conflict`); and it is in the invoice's period
(§5.3, `outside_period`).
6. **Across sources**, at `source` and the index of the later: a head contributed twice
(`record_duplicate`); a receipt, statement or ad receipt overlapping an earlier one, or, in
version 2, a route record of the router and job an earlier one names (§5.3,
`record_overlap`); and a record that is in the previous invoice's `records`, in versions 3 and
4 an output receipt excepted, in version 4 a package receipt, and in version 6 an upscale or
rendition record (§6.9, `record_rebilled`).
7. **The figures**, at `invoice`, recomputed from the sources as §6.2 to §6.6 compute them and
compared in this order:
1. a line whose source and unit no source gives, or that states a larger quantity than the
sources give: `quantity_not_recorded`, a quantity no signed record covers;
2. a row that §6.6 calls summed across classes: `classes_summed`;
3. a line whose `cited` or `cites` is not the records it came from: `citation_mismatch`;
4. any other difference (the records, a line missing, a smaller quantity, a price, an amount, a
row, `unmetered`, the `amount`): `invoice_mismatch`.
A figure that cannot be held in its field is a difference.
### 7.3 Outcome
- **refuse**: the reason and where.
- **accept**: the invoice's digest; the tariff's digest; the currency and the period; the number of
records; each line with its source kind, source, unit name (and, for a job line, the job
price's `capability`, in versions 3 and 4 its `operation`, and in version 6 its `endpoint` and,
for a usage, its `usage` as `usage_unit`), quantity, price, amount and its
cited records; each energy row by scope and class label with its figures, joules and `max_ppm`;
each scope's unmetered figures and coverage (scopes 1 to 4, in versions 2 and 3 scope 5, and in
version 4 scopes 5 and 6); the
amount; and the reports of §8.
## 8. Reports
The reports are computed from the verified sources, as the lines are, and are stated with every
accepted invoice. They are not signed: anyone holding the sources computes them, and a verifier
compares them.
### 8.1 Ad decisions against presentation claims
For each `ads` source, in order: its room, whether its policy requires claims, and each receipt in
its list's order, with its identity, decider, `cue` and `session`, and each impression's `slot`,
`ad_id`, `price_micros` and `quartiles_reached`, whether it is **billable** (the decider's beacons:
a quartile of at least 1), whether it **counts**, and its **verdict**: under a policy that requires
claims, `presented` or the reason its own claim did not count, `claim_absent` among them
(ad-presentation §9.2); under one that does not, `beacons`. Then the receipt's billable impressions
and their spend, `budget_spent_micros`, the seller's figure; and its counted impressions and their
spend, the presented spend (ad-presentation §9.3), the buyer's. Then, over the source: the
billable and the counted impressions, the billable and the counted spend, and, for each verdict
other than `presented`, the billable impressions that did not count with it.
So a seller's receipts and a buyer's claims are reconciled from the same signed bytes: what the
seller billed, what the players presented, and why each impression that was billed and did not
count did not.
For each `stitched` source, in order, the report is the statement of stitched-presentation §7.5 as
that extension's verifier gives it, unchanged: each record's verdict, each ad's presentations,
quartiles and presented spend, and, for a checked form-1 response, the receipt's own quartile and
spend beside them; each claim's verdict; and the spend by currency, never summed across currencies.
That statement is the seller-against-buyer reconciliation of a stitched stream, and the ledger
computes no second one.
### 8.2 Delivery by party
For the `delivery` sources together, each party in ascending order of key: the digests of its
statements; their `currency` (the stated one, or `null`); the acknowledged objects and bytes of
each class; the free retrieval bytes; the declared declarations, objects and bytes (settlement §4),
never billed; the sum of its statements' own `amount`s; the sum of the invoice's lines of the party;
and its energy rows and unmetered figures over its per-hop records.
Then the **overlaps**: for each pair of acknowledgements (mode 1) of two different parties, signed
by one downstream key, whose windows overlap, and each content hash that an item of each names with
byte ranges that share bytes, one overlap. It states the content hash, the downstream key, the bytes
shared, summed over every such pair of items, and the two parties with their acknowledgements'
digests, the lower party first. The overlaps are in ascending order of (content hash, downstream,
first party, first record, second party, second record). An overlap is billed by both parties, since each was acknowledged; it is what a buyer
using several delivering parties disputes with them, and the report names the signatures it rests
on.
## 9. The reference ledger service
`wai/wai-ledger` runs the ledger as an HTTP service over a directory it owns:
| request | does |
|---|---|
| `GET /v1` | the service, its seller key, its limits |
| `GET /v1/health` | `ok` |
| `POST /v1/tariffs` | `{"tariff": hex}`: a tariff whose seller is the service's key, both signatures verified (§4), kept by its digest |
| `POST /v1/sources` | `{"tariff": digest, "source": {...}}`: the source verified (§7.2 steps 5 and 6 against what the tariff's chain holds), kept by its heads |
| `POST /v1/invoices` | `{"tariff": digest, "start", "end"}`: the invoice of the period over the sources held and not yet billed, chained, signed and kept, with its outcome (§7.3) |
| `GET /v1/invoices/{digest}` | the invoice, its outcome and its verification document (§7.1) |
| `POST /v1/verify` | a verification document: its outcome |
Each refusal names its reason and place. The service writes every file whole (to a temporary name,
synced, then renamed), holds a lock on its directory so that one process writes it, and recovers
each tariff's chain and held sources from the directory when it starts. A buyer fetches an
invoice's verification document and checks it with any verifier of this extension, the service's
own not among them.
## 10. Reasons
The codes this extension adds, each with where it is raised:
| reason | raised at |
|---|---|
| `tariff_invalid` | §4.3, §7.2 step 1 |
| `invoice_invalid` | §6.8, §7.2 step 2 |
| `tariff_mismatch` | §7.2 steps 3 and 4 |
| `outside_tariff` | §7.2 step 3 |
| `invoice_overlap` | §6.9, §7.2 step 4 |
| `source_gap` | §5.1, §7.2 step 5 |
| `outside_scope` | §4.4, §7.2 step 5 |
| `tariff_conflict` | §3, §7.2 step 5 |
| `outside_period` | §5.3, §7.2 step 5 |
| `record_duplicate` | §5.2, §7.2 step 6 |
| `record_overlap` | §5.3, §7.2 step 6 |
| `record_rebilled` | §6.9, §7.2 step 6 |
| `quantity_not_recorded` | §7.2 step 7 |
| `citation_mismatch` | §6.5, §7.2 step 7 |
| `invoice_mismatch` | §7.2 step 7 |
| `private_kind` | §5.2, §6.8, §7.2 step 2 |
| `output_invalid` | §4.8, §4.9, §5.1, §7.2 step 5 |
| `job_figure_invalid` | §5.1, §7.2 step 5 |
| `output_not_delivered` | §4.8, §4.9, §5.1, §7.2 step 5 |
A verifier of this extension also raises `truncated`, `malformed` and `unknown_version` for a
tariff or an invoice that does not read, and `malformed` for a source that does not read (§5.1); `bad_key` and `bad_signature` for their signatures;
`record_missing` (live-metering §9) for a previous invoice not given; `classes_summed`
(live-metering §9) for a row over classes; and, at a source, every code its extension's verifier
raises: live-metering §9 and the codes it lists, settlement §11 and the codes it lists, packaging
§12, live-ads §10 with ad-presentation §11, and, at a `job` source, route-record §8 and the
codes it reuses. A `stitched` source raises none: the codes of
stitched-presentation §9 are the verdicts of its records and claims, stated in its report (§8.1).
## 11. Conformance
[`live-conformance/ledger/`](../live-conformance/ledger/) holds the corpus, in one file,
`vectors.json`, written by `wai_receipt_ledger_vectors gen` from fixed seeds and from sources taken
from the corpora of the extensions it reads (`live-conformance/metering/`,
`live-conformance/settlement/`, `live-conformance/ads/`, `live-conformance/presentation/`,
`live-conformance/stitched/`, `live-conformance/routes/` and `pack-conformance/packs/`), with
delivery statements of two parties signed for it and, for each media job, a sink's
`wai.video.int_motion` decode receipt of its output over a payload of
`wai-rs/tests/vectors/int_video/`, and, for version 3, the route records of a decode, a re-run, a
replayed hop receipt and a claimed one, routed and sealed under the route corpus's own seeds, as
are jobs routed under the route record's version 2, its price book of version 2 with its
per-operation entries and measured failures, the served hop stating its outputs. The sources are
held once, by name, and each vector is a verification document (§7.1) naming its sources, with the
outcome §7.3 gives it:
- **Accepted**: an invoice of one day billing a lesson room, two delivering parties and a viewer
that forwarded sealed objects under no tariff of its own, a package, ads under a policy that
requires claims and under one that does not, with both reports, one viewer acknowledging one
object from both parties among the overlaps; the next invoice of its chain, billing a relay's
session whose energy is unmetered, and a second package; and an invoice whose room hops are of
three classes, one row each; and an invoice of streams stitched by others, one with a form-1
receipt whose own figures the report states beside the presented ones; and, under a tariff of
version 2, a month of media jobs one router routed, beside a package: an `int_motion` job billed
per job and per subpixel of its output's receipt, a failover billed at the fallback's price, a
job served past its deadline and one every allowed hop failed, which bill nothing, and a job
whose serving hop was unmetered, counted unmetered in the jobs' scope; and, under a tariff of
version 3, the same jobs priced per operation with an `int_motion` decode beside the encodes,
each at its own price; the next invoice of that chain, billing the first job run again, its
route record and hop receipt new and its output receipt one the first invoice holds; the job
and its re-run in one invoice, their shared output receipt cited once and its subpixels billed
for each; and a decode under a tariff that prices only the encode, which bills nothing; jobs
routed under the route record's version 2 billed under version 3 (an encode, a failover billed
at the endpoint that served, a decode, a hop past the deadline and three that failed); and,
under a tariff of version 4, a lesson room's receipt of version 2 billed by its bytes out
beside its time, a package listed and the run of `package.build` that made it, billed per run
and per work unit, and those jobs with every hop's energy, the failed and late hops' in their
own scope, one of them unmetered; and the next invoice of that chain, billing the package it
holds for the byte-days of its own period and the same package made again by a new run, its
work billed again and its receipt cited again, beside a second package listed; and, under a
tariff of version 5, an `int_motion` decode whose served hop states it delivered one animated
PNG of the clip's frames, its receipt re-derived from the codestream it read, beside an encode
held to its codestream; and jobs whose route records are of version 3, each output proved
among what its hop delivered: an encode whose clip is the second of two objects, a decode of
version 5 whose animation is its one object, and a package's run whose receipt is the first
of 1,501 objects; and, under a tariff of version 6, a third party's jobs billed per job and by
what each record states the provider bills, each endpoint at its own price: a served hop,
another provider's, a failover whose failed hop's billed usage reaches no line, a job with no
record, a job in another unit at that unit's price, and one in a unit no price names; an encode,
a decode held to its frames and a package's run, billed as version 5 bills them; an upscale of
two pictures and two tone mappings, one of whose records states two pictures of one rendering,
each re-derived from what it read; the next invoice of that chain, billing the upscale run again
and citing its records again; and an upscale and a tone mapping whose route records are of
version 2.
- **Refused**: each reason of §10 at least once, each vector a control of one defect; a source of
each kind outside the scope, a room receipt and a statement under other prices, and a source of
each timed kind outside the period; a refusal of each source kind by its own extension; and, for
version 2, job prices out of order, a router in a version-1 tariff's scope, a job source under a
version-1 tariff, a version-1 invoice under a version-2 tariff, a job given twice, routed twice,
and claiming another job's output, and a job line and a jobs row that do not recompute; and an
invoice of each version listing a record of a kind reserved for private use, and one listing
another text's kind; and, for version 3, job prices out of order by operation, a version-2
invoice under a version-3 tariff, a hop receipt the previous invoice billed brought again
under a new route record, and one hop receipt claimed by two jobs billed on two lines; and,
under a tariff of version 2, the same re-run, refused because its output receipt was billed;
and, for a job routed under the route record's version 2, an output receipt of a clip its hop
did not deliver and one output given twice (`output_not_delivered`); and, for version 4, a
price of bytes other than the receipt's and a receipt of version 1 under a price of bytes
(`tariff_conflict`), bytes out above the receipt's, storage billed for a package first listed,
a held package's storage over the tariff's period rather than the invoice's, a package's run
replayed under a new route record in the next invoice (`record_rebilled`) and in the same one
(`record_duplicate`), a failed hop billed as a job and a job line at the router's list price, the unbilled
hops' energy left out or added to the billed hops' row, an unbilled hop whose figure states no
class (`job_figure_invalid`), an output of a route record of version 1, and an invoice of
version 3 under a tariff of version 4; and, for version 5, a decode whose record states the
codestream it read, and one whose record states it after the animation
(`output_not_delivered`), a decode in version 4's form (`malformed`), a frame with one sample
changed, two frames swapped, a still for the animation, a frame missing, frames of another
shape holding the same samples, a codestream not the receipt's, and an animation that blends
a frame over the one before, disposes of one by no defined operation, holds a frame of part of
the picture, skips a sequence number, keeps its default image apart from its frames, counts a
frame more than it holds, or has a frame with no data (`output_invalid`), a decode in
version 5's form under a tariff of version 4, and an invoice of version 4 under a tariff of
version 5; and, for jobs routed under the route record's version 3, an output without its proof,
a proof under a record of version 2 and a proof whose index is text (`malformed`), a proof of
another place, one past the count, one a sibling short, an output its hop did not deliver, one
output twice, a decode's animation at another place, a decode with an object past its animation,
and an output of a job no hop served (`output_not_delivered`); and, for version 6, job prices out
of order by endpoint, a price of any endpoint beside one, a price of usage naming no unit, a
price per job naming one, an endpoint not of its form and a unit past `usage`
(`tariff_invalid`); a price of usage under version 5, and an invoice of version 5 listing a
third-party record (`invoice_invalid`), and an invoice of version 5 under a tariff of version 6;
a job given third-party records, and an upscale and a tone mapping in version 6's form, under
version 5 (`malformed`); another job's record (`third_party_mismatch`); a failed hop's usage added to a
line, a usage line for a job with no record, and subpixels above a record's
(`quantity_not_recorded`); a usage at another endpoint's price and a record left out of the
invoice's (`invoice_mismatch`), and a usage line not citing its records (`citation_mismatch`);
an upscale whose picture is altered, of another shape or not a PNG, under another prior, of
another input, whose input is not an envelope, of another capability or not RGB, whose record
states another output, names a residual or does not verify (`output_invalid`), whose picture its
hop did not deliver or given twice (`output_not_delivered`), in an encode's form (`malformed`);
and a tone mapping with a rendition altered, one missing, renditions out of order, of another
envelope, whose record does not verify (`output_invalid`), with a rendition its hop did not
deliver or given twice (`output_not_delivered`), a proof short, without its proofs, with proofs
under a list, or in an encode's form (`malformed`).
`wai_receipt_ledger_vectors verify` and the test `tests/receipt_ledger.rs` check that the file
regenerates byte for byte, that `wai-rs` gives every vector its outcome, that every reason of §10
refuses a vector, and that §10 lists exactly the reference's codes.
`live-conformance/receipt_ledger_verify.py` is the second implementation, written from this text:
it shares no code with `wai-rs`, verifies signatures with WAI's strict rule written from RFC 8032
(`claim-conformance/_ed25519.py`), verifies each source with that corpus's own second
implementation (`live_metering_verify.py`, `settlement_verify.py`, `live_ads_verify.py` with
`ad_presentation_verify.py`, `stitched_presentation_verify.py`, `route_record_verify.py`,
`upscale_verify.py`, `tonemap_verify.py` and
`pack-conformance/pack_verify.py`) and each output receipt by `_int_motion_receipt.py`, written
from integer-payloads §15, computes each output's content hash with BLAKE3 written from its
specification (`claim-conformance/_blake3.py`), recomputes every line,
citation, row and report itself, gives every vector the same outcome, and requires every mutated
copy of the corpus to fail.
## 12. Security and privacy considerations
- **Who signs what.** The buyer signs the prices and the scope; each record's own party signs its
figures; the seller signs the invoice. A seller cannot bill a unit at a price the buyer did not
sign, a record of a party the buyer did not name, or a quantity no record states: the verifier
recomputes every line.
- **Double billing.** A head is billed once in an invoice (`record_duplicate`), one session, one
statement period and one viewer's break once (`record_overlap`), and a record once in a chain
(`record_rebilled`). A verifier given one invoice checks it against the one before it; a buyer
holding the chain checks every link.
- **Work, not identity.** Under a tariff of version 3 a job is billed for the work done, its
route record and its hops' OER/2 receipts, once per chain, and its output's receipt is the
evidence of what it delivered (§6.9). A deterministic re-run does the work again, a new hop,
new energy, a new receipt, and is billed; a replayed hop receipt or route record is refused,
which is what keeps a job from being billed twice. A seller cannot bill a job again by citing
its output again: every billing needs a hop receipt no invoice of the chain holds. What a re-run
rests on is that hop's receipt: a router and a hop that collude can bill a re-run that never
ran, as a party and a downstream key can bill bytes (settlement §13). Whether a request is
worked again or answered from outputs still kept is the router's decision, not the ledger's;
the ledger bills what was worked. A tariff of version 2 keeps billing an output once.
- **Bytes, work and storage.** Under a tariff of version 4 a room's bytes out rest on its hops'
ledgers, which each hop signs by their root (live-metering §3.4), as its time rests on their
records; a package's work rests on the run that made it, its route record and its hop's
receipt, new on every run, and on its receipt's work units, which its indexes recompute; and
its storage on the chain: a package is billed a period's storage only when the invoice before lists it,
so it was written by the start of the period billed, and a buyer holding the chain sees each
period billed once. Whether the seller kept the bytes through the period is the seller's
statement, as a delivery is the acknowledging parties' (settlement §13).
- **A third party's usage.** Under a tariff of version 6 a job may be billed by what a third
party's record states the provider bills. That figure is the router's statement: no provider
signs anything, so the record holds what the router read, with the provider's response kept
where it was JSON and every byte named by digest (route-record §6.2), and a router can state a
usage the provider did not bill. A buyer holds the price it signed and the record the router
signed, each line cites that record, and the provider's own `charge` is stated beside it, so a
dispute is about a signed figure; it is not recomputable, as a re-derived subpixel is.
- **What re-derives an output.** Under a tariff of version 6 an upscale's and a tone mapping's
subpixels are recomputed by the verifier from the record, what it read and what the hop
delivered. That binds the picture to its input, and nothing binds the input to what the buyer
asked for: the hop's receipt names its inputs by digest, which a holder of them checks.
- **Every hop's energy.** Under a tariff of version 4 an invoice states the energy every hop of
its jobs spent, the hops the buyer was not billed for in a scope of their own, so a seller
whose router fails often shows it in the energy it states, and is not paid for it.
- **What the figures rest on.** An invoice is as true as its records: a delivery line rests on
acknowledgements, so a party and a downstream key that collude can bill bytes that never moved,
as settlement §13 says; a room line rests on per-hop records and the room's events; an ad line
under claims rests on players' session keys. The reports name the signatures each figure rests
on, so a dispute is about records, not about logs.
- **Energy.** A row states figures as their parties signed them, with their class: it is
attributable and its sum recomputable, not true (energy-measurement §5). `max_ppm` bounds the
declared part of a sum's uncertainty and says nothing of a class's undeclared systematic error.
- **What an invoice reveals.** Its records list names every record by identity, and its lines name
rooms, parties, packagers and deciders. A verification document holds the records themselves:
acknowledgements name downstream keys, and ad receipts name sessions. They travel between the
seller, the buyer and an auditor they choose; the service serves an invoice's document by its
digest and lists nothing.