Skip to main content

WAI Extension: Delivery Settlement

Mirrored from the canonical text at commit b1681667 ().

Status: Draft. Registers the record wai.delivery.settlement: delivery paid on what the party that received it signed, not on what the party that sent it logged. The party downstream of a delivery, a next hop or a sink, signs an acknowledgement, WDA1, of what it received in a window: per object class, the objects by content hash under jwp-receipts §2.2’s tree, the byte ranges of each, and their byte count. A relay is the downstream party of the acknowledgement it signs upstream and the upstream party of those its downstream sign for it, and both name the objects by the same content hashes. A hop whose downstream will not or cannot sign states that delivery in a declaration, the same record signed by the hop itself, which is counted in a class of its own, unacknowledged: never as zero and never as acknowledged. A byte-range proof, WBR1, shows that given bytes are a given range of an object without the rest of it, over BLAKE3’s chunk tree. A retrieval that costs money (a fetch below the live edge, a recording fetch, an origin fill past a cache) is billed only to a grant whose holder’s proof of possession the request carried (live-rooms §10.5); an ungranted retrieval is free. Per party and per period, a settlement statement, WSS1, states the acknowledged bytes by object class, the declared bytes apart, the party’s energy by acquisition class from its per-hop records (live-metering §3), never summed across classes, and the amount under a declared tariff in integer micro-units; a verifier recomputes every figure from the acknowledgements, and refuses a statement that does not recompute. A statement’s attribution lines pay the owners of the works that conditioned an output from its acknowledged deliveries, under a basis influence verifies. No new transport, no payment rail and no money. The reference implementation is the settlement module of wai-rs (feature settlement); the corpus is live-conformance/settlement/, and its second implementation is live-conformance/settlement_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.

Texts read for this revision: the WAI texts this one cites; RFC 7937 (§3.2 to §3.4.1, the logging file, its directives and the cdni_http_request_v1 record); RFC 9246 (§4.5, the logging fields of URI Signing, and §6.2.1, the record type cdni_http_request_v2); the BLAKE3 specification (its chunk chaining values, parent nodes and ROOT flag); RFC 6962 §2.1; RFC 8032; FIPS 180-4. Of SVTA2050, SVTA2007 and SVTA2006 only their published summaries were read; §9.3 says what follows from that.

1. Scope

In scope: the acknowledgement, its entries, its object classes, its items, its signature, its rules and its chains (§3); the declaration of a delivery nobody acknowledged (§4); the byte-range proof and the audit it answers (§5); who may trigger a retrieval that costs money, and how an acknowledgement records it (§6); the settlement statement, its lines, its tariff, its energy, its attribution lines and its recomputation (§7); verification (§8); the mapping onto CDNI and open caching logs (§9); and a viewer that relays objects it cannot read (§10).

Out of scope: a transport; money, a payment rail, an invoice and any contact with a party outside the records; how a party chooses its tariff; how a hop measures its energy, which energy-measurement governs; the delivery receipts themselves, which jwp-receipts defines and which a downstream party checks before it acknowledges anything; and the attribution an attribution line is paid from (§7.8), which influence defines and verifies.

Relationship to the core spec: wai.delivery.settlement is a record (SPEC §5): a signed record a WAI tool emits and verifies. It names no payload format and is not dispatchable.

2. Terms

3. The acknowledgement

3.1 Layout

fieldsizemeaning
magic4WDA1 (ASCII)
version11 in this revision
mode11 an acknowledgement, signed by downstream; 2 a declaration (§4), signed by upstream
role1the downstream party’s role: 1 a hop, 2 a sink
upstream32the key of the party that delivered
downstream32the key of the party that received; in a declaration, zero for a downstream party with no key the upstream knows
start, end8 eachu64: the window the record covers
prev32the digest of the record before it in its chain (§3.7); zero for the first
entries2 + 141 × na u16 count n, then each entry (§3.2)
sig64§3.5

3.2 Entries

An entry states what the downstream party received in the window of one object class, under one grant and one proof:

fieldsizemeaning
class1the object class (§3.3)
grant32zero, or the digest of a room grant (live-rooms §4.2): for a delivery class the grant the downstream party received under, for a retrieval class the grant the retrieval is billed to (§6)
proof32zero, or the SHA-256 of the proof record (WRP1, live-rooms §10.5.1) the retrieval’s request carried (§6)
objects4u32: the items of the entry (§3.4)
bytes8u64: the sum of its items’ byte counts
root32the tree root of its items’ content hashes, in the items’ order
items32SHA-256 over the 17 bytes wai:settle-items (ASCII) and 0x01, followed by its item list (§3.4)

3.3 Object classes

classnamewhat it counts
1livemedia objects delivered to a subscription at or after the live edge, as they were published
2fetchobjects served from a store in answer to a request that seeks back below the live edge (live-rooms §10.6) or that fetches a recording (live-recording §8)
3fillobjects a cache or a relay retrieved from its upstream to answer a request it could not answer from what it held
4ancillaryevents, catalogs, receipts tracks, timed text and every other object that is not media

Classes 1 and 4 are delivery classes; classes 2 and 3 are retrieval classes, which cost the serving party a store or an upstream fetch and are billed only to a grant (§6). An entry of class 3 is signed by the cache or relay that filled, for its upstream, and is billed to the grant of the request it was filling for.

3.4 Items

An entry’s item list is the concatenation of its items, each 48 bytes:

fieldsizemeaning
content_hash32the object’s content hash
start, end8 eachu64: the byte range of the object’s envelope the downstream party received; a whole object is start 0 and end its length

An item’s byte count is end − start. An object received in parts (an HTTP range request, a fill that stopped) is acknowledged by the ranges received. A downstream party lists the items of a whole group in the group’s order, so that its entry’s root is the root the group’s publisher signed (jwp-receipts §2.3) when the entry holds exactly that group, whole and unlabelled. The item lists travel beside the record, unsigned; the record signs each list’s digest and the root over its content hashes, so the list can only be the one its signer signed, and one object’s place in an entry can be shown by its jwp-receipts §2.4.2 inclusion proof against root, without the other items.

3.5 Digest and signature

The digest of a record is SHA-256 over its bytes before sig. Its signature is Ed25519 over the 15 bytes wai:settle-ack (ASCII) and 0x01, followed by the bytes the digest covers, by downstream in an acknowledgement and by upstream in a declaration, verified strictly as SPEC §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.

3.6 Reading and rules

A reader reads exactly the bytes it is given as one record, in the order of §3.1, and refuses at the first of these that holds: fewer than 4 bytes, truncated; a magic other than WDA1, malformed; no fifth byte, truncated; a version other than 1, unknown_version; the bytes end before a field the layout requires, truncated; bytes after sig, malformed. It then refuses as ack_invalid a record that breaks any of these:

An acknowledgement whose downstream is its upstream is refused self_acknowledged: a party’s statement that it received what it delivered is not an acknowledgement, and a hop states its own delivery only in a declaration (§4). Then the signature (§3.5): bad_key, bad_signature.

An item list is read as items of 48 bytes, its length a multiple of 48, each item’s start below its end, and no two items sharing a byte of one object (the same content hash, with ranges that overlap): otherwise items_invalid. It recomputes against its entry when it holds objects items, the sum of their byte counts is bytes, the tree root of their content hashes in order is root, and its digest is items: otherwise items_mismatch. The sum is taken in integers wide enough not to overflow.

3.7 Chains

A chain is the records of one mode, one upstream and one downstream. Its records cover windows that do not overlap, and each names in prev the digest of the record of its chain whose window comes before its own; the first record of a chain names zero. So a party’s acknowledgements to one upstream are a sequence, and an acknowledgement signed for one window cannot stand for another: the window is signed, and so is its place in the chain.

Through a relay. A relay is the downstream party of the acknowledgement it signs for its upstream, and the upstream party of the acknowledgements its own downstream parties sign for it. A relay forwards envelopes unaltered (jwp-receipts §4.1), so the two name the objects by the same content hashes: what a relay is paid for can be matched, object by object, against what it acknowledged receiving. A deriving edge (deriving-edge) is paid for objects of its own, which its derivation records name with the inputs it acknowledged. A hop’s billable bytes are exactly the bytes its downstream parties signed for: the sum of the bytes of the entries of the acknowledgements naming it as upstream (§7.6).

4. Unacknowledged delivery

4.1 Declarations

A downstream party may not sign: a player that holds no key, a viewer admitted by Privacy Pass (live-rooms §10.4), a sink that will not, or a link with no return path. The hop that delivered to it states that delivery in a declaration: a record of §3’s layout with mode 2, signed by the hop, naming the downstream party’s key where it has one and zero otherwise. A declaration’s entries and items keep §3’s rules. Deliveries to downstream parties with no key form one chain per hop, so in one window they are stated together, in one declaration.

4.2 What a declaration is

A declaration is the delivering party’s own statement. Its bytes are counted in a class of their own, unacknowledged:

4.3 Bearers with no return path

A unidirectional bearer settles under unidirectional-delivery §4: what a transmitter sent over it is declared, never acknowledged, and a sink’s later reconciliation is reported apart from the chain as that section requires. This extension adds nothing for it.

5. Byte-range proofs

5.1 The tree

An object’s content hash is the BLAKE3 root, in hash mode, over 1024-byte chunks of X = "moq-jwp:object\x01" ‖ envelope, the chunk at byte offset 1024 × i of X having chunk counter i. With L = 15 + the object’s length, left(n) = 1024 · 2^⌊log₂((n − 1)/1024)⌋ for n above 1024 splits a subtree of n bytes into its left and right children (http-delivery §6.3, whose head proof is the case of a range that starts at 0). A range proof shows that the bytes it carries are the bytes at given offsets of the object with a given content hash: it carries the chunks that hold the range, and the chaining value of every subtree of X that lies wholly outside them.

5.2 Layout

fieldsizemeaning
magic4WBR1 (ASCII)
version11 in this revision
content_hash32the object’s content hash
envelope_len8u64: the object’s length
start, end8 eachu64: the byte range of the envelope it proves
n_cv1u8: the chaining values it carries
cvs32 × n_cvthe chaining values, in the order §5.4 consumes them
bytesthe restthe covered bytes (below)

With c0 = 1024 · ⌊(15 + start) / 1024⌋ and c1 = min(L, 1024 · ⌈(15 + end) / 1024⌉), the covered chunks are the bytes of X from c0 to c1, and bytes is the part of them that is envelope: the envelope’s bytes from max(c0, 15) − 15 to c1 − 15. A verifier knows the 15 bytes of the domain and never reads them from the record.

5.3 Reading

A reader refuses at the first of these that holds: fewer than 4 bytes, truncated; a magic other than WBR1, malformed; no fifth byte, truncated; a version other than 1, unknown_version; fewer than 62 bytes, truncated; start not below end, range_invalid; end above envelope_len, or L past what a u64 holds, range_outside_object; fewer bytes than 62 + 32 × n_cv + the covered length (bytes above), truncated; more, malformed.

5.4 Verification

A range proof is checked against the content hash H of the object it is offered for. With the covered chunks of X held, and in order:

  1. its content_hash is H: otherwise range_proof_mismatch;
  2. where it answers an audit (§5.6), its range is within the audited item’s: otherwise range_not_acknowledged;
  3. the root computes to H, consuming its chaining values exactly: otherwise range_proof_mismatch. When c0 is 0 and c1 is L, the covered chunks are the whole of X: n_cv is 0 and the root is BLAKE3 of X. Otherwise
cv(s, n):
  if s + n ≤ c0 or s ≥ c1:     take the next chaining value from cvs
  elif c0 ≤ s and s + n ≤ c1:  the non-root chaining value of the n bytes of X at s,
                               a subtree whose first chunk has counter s / 1024
  else:                        parent_cv(cv(s, left(n)), cv(s + left(n), n − left(n)))
root = parent_root(cv(0, left(L)), cv(left(L), L − left(L)))

parent_cv and parent_root are BLAKE3’s parent node in hash mode, without and with the ROOT flag. c0 is a multiple of 1024, and so is c1 unless it is L, so a subtree the procedure meets lies wholly inside the covered chunks or wholly outside them once it is split far enough; a single chunk is never split. A chaining value left over, or one missing, is range_proof_mismatch.

5.5 What a proof establishes

A proof that passes establishes that the bytes from start to end it carries are those bytes of the envelope whose content hash is H. The root does not encode the object’s length, so envelope_len is established only when c1 is L, when the proof’s last covered chunk is the object’s last: a tail proof. A proof of a range that ends before the last chunk establishes the tree’s shape and not the length (as http-delivery §6.3 check 3 notes of a head proof), and a verifier reports whether the length was established. A proof is a pure function of the object and the range: anyone holding the object can make one, and a proof over bytes that are not the object’s at those offsets fails step 3.

5.6 Audits

An auditor (the party paid, the party paying, or a party settling between them) may name an item of an acknowledgement and a range within it, and ask the downstream party that signed it for a range proof. The audit passes when the proof verifies against the item’s content hash (§5.4) and its range is within the item’s; a downstream party that cannot answer has not shown that it holds the bytes it signed for. A proof carries the covered chunks, so an audit costs the range rounded out to whole chunks and two chaining values per level of the tree, not the object: a few chunks drawn at random audit a large object. Over a sealed object the proof is over the sealed bytes, which a party that cannot open them can prove as well as one that can (§10).

6. Paid retrieval

6.1 The rule

A hop MUST NOT perform a retrieval of a retrieval class (§3.3) that costs money (a fetch from its store below the live edge or of a recording, a fill from its upstream past what it holds) on a request that carries no grant and no proof of possession of it (live-rooms §10.5). An ungranted request is served free from what the hop holds, or refused, as the hop’s policy says: a relay that seeks back refuses it (live-rooms §10.6, seek_not_granted), and an HTTP store may serve a preview free. A retrieval performed on an ungranted request, a fill among them, is the serving hop’s own cost and is never billed to a grant holder.

6.2 Recording the grant

The acknowledgement of a retrieval names, in its entry, the grant the retrieval is billed to and the proof the request carried: grant the grant’s digest, proof the SHA-256 of the WRP1 record. Over MOQT the proof is the session’s GRANT_PROOF (live-rooms §10.5.2), one for every retrieval of the session; over HTTP it is the request’s WAI-Room-Proof (live-rooms §10.5.3). A sink that fetched names its own grant and proof. A cache that filled for a request names the grant and the proof that request carried, and hands the grant and the proof records to its upstream beside its acknowledgement. An entry of a retrieval class with a zero grant records a retrieval no grant was presented for: it is acknowledged, and free (§7.6).

6.3 Checking

For each entry naming a grant, a verifier checks, in order:

  1. the grant is among the grants of a room it is given (§8.1): otherwise record_missing;
  2. the grant is good throughout the record’s window, nbf × 10^6 ≤ start and end ≤ exp × 10^6: otherwise outside_grant_window;
  3. for a delivery class, the grant’s principal is the record’s downstream: otherwise grant_wrong_principal;
  4. for a retrieval class, the proof whose SHA-256 is proof is given (record_missing), and it passes live-rooms §10.5.4 steps 2 to 5 for the grant: it reads (pop_malformed), names the grant (pop_wrong_grant), its key is the grant’s principal (pop_wrong_key), and its signature verifies (pop_bad_signature);
  5. for class 2, the grant holds the seek power (live-rooms §3.3, bit 4): otherwise seek_not_granted.

Steps 6 and 7 of live-rooms §10.5.4, the challenge and the context, were the serving hop’s when the request arrived: a verifier of a statement holds neither the challenge nor the request. What it holds instead is the rule that one presentation pays for what one session or one request retrieved: a proof named by entries of two records is refused pop_replayed (§8.2 step 4). Revocation through the room authority’s key log (live-rooms §5) was the serving hop’s to check at the request, and is not checked again.

7. The settlement statement

7.1 Layout

Per party and per period, the party signs one statement of what it is owed:

fieldsizemeaning
magic4WSS1 (ASCII)
version11 in this revision
party32the key the statement settles, which signs it
start, end8 eachu64: the period
records2 + 32 × na u16 count, then the digests of the acknowledgements and declarations naming party as upstream that it settles, in strictly ascending order
hops2 + 32 × ka u16 count, then the digests of the party’s per-hop records (live-metering §3), in strictly ascending order
tariff3 + 32currency (three capitals A–Z, ISO 4217; three zero bytes for no tariff), then four u64 prices, one per object class in class order, each in millionths of a minor unit of currency per 10^9 bytes
classes64for each object class in order, two u64: the acknowledged objects and bytes
unacknowledged20declarations (u32), then objects and bytes (u64 each): the declared delivery (§4)
unmetered2u16: the hop records whose figure is the unmetered marker
totals1 + 13 × ma u8 count, then for each acquisition class among the metered hop records: tag (u8), figures (u32) and joules (u64, µJ), in strictly ascending order of tag
lines2 + 90 × pa u16 count, then each line (§7.2)
amount8u64: the sum of the delivery lines’ amount, in millionths of a minor unit: what the party is owed
sig64§7.4

7.2 Lines

A line is 90 bytes:

fieldsizemeaning
kind11 a delivery line; 2 an attribution line (§7.8)
payee32the key paid
class1the object class
payer32who the line is billed to (below)
objects8u64: the acknowledged objects
bytes8u64: their acknowledged bytes
amount8u64: the line’s amount, in millionths of a minor unit

A delivery line (kind 1) has payee the statement’s party, and states, for one object class and one payer, the objects and bytes of the entries of the statement’s acknowledgements of that class billed to that payer. The payer of an entry of a delivery class is its record’s downstream, the party that received; of an entry of a retrieval class, its grant, the grant the retrieval is billed to, or zero for a retrieval no grant was presented for. A line of a retrieval class whose payer is zero is free.

An attribution line (kind 2) pays a party that is not a hop, the owner of a work that conditioned an output, from the output’s acknowledged deliveries: its payee is the key paid, its payer the basis it is paid from, an attribution influence §6.1 defines, named by its link hash, and its objects, bytes and amount are §7.8’s. It is what the statement’s party owes, not what it is owed, so it is never in classes or amount. It sits beside the delivery lines with no change to the layout.

7.3 Tariff and amounts

The tariff gives each object class a price in millionths of a minor unit of currency per 10^9 bytes. A line’s amount is ⌊bytes × p / 10^9⌋ with p its class’s price, computed in integers wide enough not to overflow, and 0 for a free line. The statement’s amount is the sum of its lines’ amounts. With no tariff every price is 0 and so is every amount. The smallest price, 1, is one thousandth of a minor unit per 10^12 bytes. An amount is rounded down once per line, so a statement never states more than its bytes at its prices.

7.4 Digest and signature

The digest of a statement is SHA-256 over its bytes before sig; its signature is Ed25519 by party over the 21 bytes wai:settle-statement (ASCII) and 0x01, followed by the bytes the digest covers, verified strictly (SPEC §7 Signatures).

7.5 Reading and rules

A reader reads exactly the bytes it is given as one statement, as §3.6 reads a record with WSS1 for WDA1 (truncated, malformed, unknown_version), and refuses as classes_summed a statement whose totals names tag 0: a total over figures of no one class (live-metering §4.2). It refuses as statement_invalid a statement that breaks any of these:

Then the signature (§7.4): bad_key, bad_signature.

7.6 Recomputation

Every figure of a statement is recomputed from the records it names:

A verifier compares in this order and refuses at the first difference:

  1. a free line whose amount is not 0: paid_without_grant;
  2. a delivery line whose class and payer no entry gives, or that states more objects or more bytes than its entries give, and an attribution line whose basis’s output has no delivery in its class or fewer deliveries or bytes than it states: bytes_not_acknowledged, the bytes a statement bills that no acknowledgement covers;
  3. a total that live-metering §4.5 calls summed across classes: classes_summed;
  4. any other difference, a line missing, fewer bytes than acknowledged, an amount, a class, the declared figures, unmetered, a total: statement_mismatch.

A figure that cannot be held in its field is a difference.

7.7 Energy

A statement states the party’s energy over the period from its per-hop records (live-metering §3) as live-metering §4.5 states a session’s: unmetered counts the records whose figure is the unmetered marker, which are in no total and are never presented as zero joules; each total is one acquisition class’s number of figures and their sum; and nothing sums the figures of two classes. A record counts when its hop is the party and its span, placed in time by its room’s t0, lies within the period: t0 + its start ≥ the period’s start and t0 + its end ≤ the period’s end. The energy is whole when unmetered is 0 and at least one record is named, partial when a named record is unmetered, and none when no record is named. Joules are not divided among lines or bytes in this revision: an energy figure says what the party spent, and a line says what it is owed.

7.8 Attribution lines

A basis (influence §6.1) gives: an output’s content hash H; its whole T, in units; and its payable buckets, each a key, its units u (at most T), a per-use price f (an amount, 0 to 2^53 − 1) and a share s in parts per million (0 to 1 000 000), f in millionths of a minor unit of the statement’s currency. For the basis and an object class c, over the statement’s acknowledgements (mode 1):

For each key with a payable bucket in the basis and each class with D above 0, there is one attribution line: payee the key, class c, payer the basis, objects D, bytes Y, and amount the sum over the key’s buckets of

f × D + ⌊R × s × u / (1 000 000 × T)⌋

computed in integers wide enough not to overflow. Each bucket’s revenue share is rounded down once, so no payee is paid more than its share at its price; what the rounding leaves stays with the party, and is never added to another payee’s line. No attribution line is computed from a declaration, and a key with no payable bucket in the basis has none.

8. Verification

8.1 Inputs

A verifier is given the statement; the records it names, each with the item list of each of its entries; the proof records (WRP1) its entries name; the per-hop records it names; the rooms the grants and hop records come from, each a room descriptor (live-rooms §3) and the room’s grants in order (live-rooms §4.3); and the bases its attribution lines name, each verified as influence §6.2 verifies it.

8.2 Steps

It runs these steps and stops at the first refusal, which it reports with the reason and where:

  1. The statement (§7.5), at statement: truncated, malformed, unknown_version, classes_summed, statement_invalid, bad_key, bad_signature.
  2. The rooms, at room and the room id: each descriptor reads and verifies (live-rooms §3.4, with its reasons), and its grants check (live-rooms §4.3, with its reasons, at grant and the grant’s index).
  3. Each record, in the statement’s order, at record and its index:
    1. a record whose digest it names is given: otherwise record_missing;
    2. it reads and verifies (§3.6): truncated, malformed, unknown_version, ack_invalid, self_acknowledged, bad_key, bad_signature;
    3. its upstream is the statement’s party: otherwise ack_wrong_party;
    4. its window is within the period, start ≤ its start and its end ≤ end: otherwise ack_outside_period, an acknowledgement replayed from another period among them;
    5. each entry, in order, at entry and its index: its item list is given (record_missing), reads (items_invalid) and recomputes (items_mismatch); and, where it names a grant, §6.3 steps 1 to 5.
  4. The chains, at record and an index. Order each chain’s records (§3.7) in the statement by (start, index). Then, for each record in the statement’s order that has a record p before it in that order, p the last such: its window overlaps p’s (p’s end is above its start), ack_overlap; or its prev is not p’s digest, ack_chain_broken. Then, in the statement’s order, each declaration that breaks §4.2’s last rule is declared_and_acknowledged. Then, in the statement’s order of records and each record’s order of entries, an entry naming a proof that an entry of an earlier record names is pop_replayed, at entry and its index.
  5. Each hop record, in the statement’s order, at hop and its index: a record whose digest it names is given (record_missing); it reads as live-metering §3.6 reads a record of the room its room field names, with that section’s reasons; that room is given (record_missing); its hop is the party (hop_not_party); and its span is within the period (§7.7, span_outside_period).
  6. The bases (§7.8), at basis and an index k: for each payer of an attribution line, in the order the lines first name it, the k-th, the basis is given (record_missing). A verifier of this extension alone holds no basis; influence §6.2 gives each, verified.
  7. The figures (§7.6), at statement: paid_without_grant, bytes_not_acknowledged, classes_summed, statement_mismatch.

A verifier finds a record, a hop record or a proof among the bytes it is given by digest: the digest of given bytes, of a record, a hop record and a descriptor alike, is SHA-256 over all but their last 64 bytes, and of a proof record SHA-256 over all of it. A room that does not read is named by the digest of its descriptor’s bytes.

8.3 Outcome

A range proof’s verifier (§5.4) reports refuse with its reason, or accept with the range, envelope_len, the covered chunks, and whether the length was established (§5.5).

9. Logs

9.1 CDNI logging records

A CDN that settles under this extension and exchanges logs under RFC 7937 maps each item of an acknowledgement of an HTTP delivery onto one cdni_http_request_v1 record (RFC 7937 §3.4.1), or cdni_http_request_v2 where it uses URI Signing (RFC 9246 §6.2.1):

RFC 7937 fieldwhat it carries here
date, time, time-takenwhen the surrogate finished the request; the record’s window holds that instant
cs-method, protocol, sc-statusthe request; sc-status 200 for an item of a whole object, 206 for a range, whose Content-Range gives the item’s start and end
cs-uri, u-urithe object’s URI; under http-delivery §5.2 it is <cid>.wai, and the cid is the item’s content_hash
sc-entity-bytesthe item’s byte count
sc-total-bytesthe response’s bytes with its status line and headers, which an acknowledgement never counts
s-cached1 for an item of class 1, 2 or 4 served from what the surrogate held; 0 when it filled, whose fill is a class 3 entry of the surrogate’s own acknowledgement upstream
c-groupid, s-ip, s-hostname, s-portno counterpart: an acknowledgement names the downstream party by its key, never by an address
s-sid, s-ccidno counterpart
cs(WAI-Room-Proof)where request headers are logged, the proof of a retrieval (§6.2); the record’s proof is its SHA-256
s-uri-signing (RFC 9246 §4.5)200 when the request’s URI Signing JWT, mapped from a grant (live-rooms §10.3), verified

The acknowledgement’s digest travels in a remark directive (RFC 7937 §3.3), #remark: wai-settle-ack <digest in hexadecimal>, before the records of its items. RFC 7937’s tools ignore a remark, and a settlement verifier reads it to find the record a log line is covered by.

9.2 What a log cannot carry

Every record of an RFC 7937 file is the serving side’s statement. The file’s SHA256-hash directive is a hash its writer computes over the file, which detects truncation and carries no key; claimed-origin is the writer’s claim of who it is, and established-origin the receiver’s note of whom its transport authenticated (RFC 7937 §3.3). No field carries a signature by the party that received the bytes, and no field binds the bytes served to a content hash. A log is therefore what a declaration is (§4): the delivering party’s own word. A converter maps a log onto declarations, never onto acknowledgements; an acknowledgement’s counter-signature, the downstream party’s signature over the content hashes and byte counts, is what no log field expresses, and it travels beside the log as the record.

9.3 Open caching logging and request routing

SVTA2050 extends RFC 7937’s pull mode with batch push and streaming transfer, and defines record types, one of them minimal for billing; it extends the footprint and capabilities interface to advertise logging. Its body was not read for this revision, only its published summary, so this revision maps its records only through the RFC 7937 fields they carry (§9.1); a field it adds is not mapped, and §9.2 holds for it as for any log a serving side writes. SVTA2007 and SVTA2006 define how an upstream CDN delegates requests to an open cache and provisions that delegation. A request router that delegates or redirects a request moves no bytes and is not a party of a settlement. A redirected request reaches a new target, so it carries a proof over the new target’s challenge (live-rooms §10.5.3), and a cache that fills for it records that proof (§6.2).

10. Viewers as relays over ciphertext

A viewer that forwards objects to other viewers is a hop for them: it is the upstream party of the acknowledgements they sign, and it is paid for the bytes they acknowledge, on the terms of any relay. In an end-to-end encrypted room (confidentiality) the objects it forwards are sealed, and their content hash is over the sealed bytes (confidentiality §8.1):

The corpus holds a viewer’s statement for sealed objects it forwarded, and a range proof over a sealed object.

11. Reasons

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

reasonraised at
ack_invalid§3.6, §8.2 step 3
self_acknowledged§3.6, §8.2 step 3
items_invalid§3.6, §8.2 step 3
items_mismatch§3.6, §8.2 step 3
ack_wrong_party§8.2 step 3
ack_outside_period§8.2 step 3
ack_overlap§3.7, §8.2 step 4
ack_chain_broken§3.7, §8.2 step 4
declared_and_acknowledged§4.2, §8.2 step 4
range_invalid§5.3
range_outside_object§5.3
range_proof_mismatch§5.4
range_not_acknowledged§5.4, §5.6
statement_invalid§7.5, §8.2 step 1
paid_without_grant§6.1, §7.6
bytes_not_acknowledged§7.6
statement_mismatch§7.6
hop_not_party§7.7, §8.2 step 5
span_outside_period§7.7, §8.2 step 5

A verifier of this extension also raises truncated, malformed and unknown_version for a record, a range proof or a statement that does not read; bad_key and bad_signature for their signatures; record_missing (live-metering §9) for an input not given; classes_summed (live-metering §9) for a total over classes; live-rooms §11’s outside_grant_window, grant_wrong_principal, seek_not_granted, pop_malformed, pop_wrong_grant, pop_wrong_key, pop_bad_signature and pop_replayed for a grant and its proof; every code a descriptor and its grants raise (live-rooms §3.4, §4.3), at room; and every code live-metering §3.6 raises for a per-hop record, at hop.

12. Conformance

live-conformance/settlement/ holds the corpus, in one file, vectors.json, written from fixed seeds by wai_settlement_vectors gen. It holds objects, rooms, acknowledgements and declarations with their item lists, proof records, per-hop records, and:

A conforming verifier gives every statement and every range proof the outcome, the reason, the place and the figures the corpus states. wai_settlement_vectors verify and the test tests/settlement.rs check that the file regenerates byte for byte, that wai-rs gives every vector its outcome, that every reason of §11 refuses a vector, and that §11 lists exactly the reference’s codes. live-conformance/settlement_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), computes BLAKE3 and its chaining values with claim-conformance/_blake3.py (the module the HTTP head proof’s verifier reads its tree from), reads rooms and grants with live_rooms_verify.py and per-hop records with live_metering_verify.py, recomputes every figure itself, gives every vector the same outcome, requires every mutated copy of the corpus to fail, and requires the corpus to fail a copy of itself with each of its checks dropped.

13. Security and privacy considerations