The change of wai/one-way-delivery (f1258784d..987625394, 28 commits), on the fix branch: its changelog entries as files of wai/changes/, its wai-conformance.yml edits as step files, corpus files and test tags, and every other file as the branch has it. - 25240c4f0 wai: timeliness — when each object of a group is of use, and whether each holder held it in time - c84992362 wai: timeliness_verify.py — the timeliness corpus's second implementation - 44036fcdc wai: timeliness in the registry, SPEC §5, the changelog and CI; http_receipts.py checks its claims - 27ce6754c wai: claim_verify.py lists not-held-by-deadline, and the claim corpus holds a session that switches for it - 73d8d8eb9 wai: timeliness corpus — each holder's own line in key order, a holder's earliest fallback, and §7.2–§7.3's unreached plan rules - 64c735f0c wai: the files timeliness changes are clippy-clean — a let chain, and the fetch callback's type allowed - b72dc36bf wai: rung price — what delivering a rung costs, by bearer and direction, and which option a comparer takes - 4e905e213 wai: rung_price_verify.py — the rung price corpus's second implementation - 711fa6126 wai: rung price in the registry, SPEC §5, the changelog and CI - 8ab70654e wai: rung price corpus — a refusal for every form rule 1 and §6.1 set, and the neighbour order a plan gives - 1bc465959 wai: rung price ranks energy by checked evidence — a class counts only on the seller's own per-hop records, current and under a confirmed key - 9e97997d9 wai: rung_price — the evidence text bound at its exact sizes, and a key confirmed for another class confirming nothing - 8f1168683 wai: rung price corpus — a key confirmed for another class, the uncertainty product, each edge and order of the evidence verdict, and the evidence a plan read - 7330e26ff wai: rung price — §6.1 links jwp-receipts at its first mention, and §10 says a record's age is its room's word - 12c099457 wai: cache authority — who served what a node holds, and on whose authority - f8f5d3e09 wai: cache_authority_verify.py — the cache authority corpus's second implementation - 354146619 wai: cache authority in the registry, SPEC §5, the changelog and CI; its corpus holds what §12 states - 55c7f3031 wai: timeliness §6 reads holdings records alone; cache-authority §9 derives the lines that read every kind - b8fc6f02b wai: cache authority's lines read records as timeliness does, its deadline claim meets the envelopes held, and its corpus fixes each order and instant - 91c5291cf wai: the files cache authority changes are clippy-clean — a boxed envelope, the base's names named once, slices borrowed - 8e479daff wai: a held envelope boxes both its forms, so neither outweighs the other - 3ec8da807 wai: impression aggregates — ad presentations counted over a bearer with no return path - 0433a35c3 wai: impression_aggregate_verify.py — the impression aggregates corpus's second implementation - 7d8e150e4 wai: impression aggregates in the registry, SPEC §5, the changelog and CI; its corpus states each receipt whole and a control for every malformed file - d5e8ace02 wai: impression aggregates' size bounds are checked at their edge, and §4.3's decides no outcome - 4bc8e13bd wai: impression aggregates' table totals are exact, and a receipt given twice is checked when either occurrence was - 3c9f20524 wai: key_log's two result enums keep their public form, and say so to clippy - 987625394 wai: Item::with_authority and Count::instance_received build only with the feature that calls each Co-Authored-By: Claude Opus 5.5 <[email protected]>
51 KiB
WAI Extension: Cache Authority
Status: Draft. Registers the record
wai.delivery.cache: who served what a node holds, and on whose authority. A neighbourhood cache or a peer that serves, from what it holds, objects of groups another key signed changes no byte: the publisher's receipts verify whatever it serves (jwp-receipts §3), and a receiver's acknowledgement of it (settlement §3) is already the signed word that it served. What was missing is the authority to place such a node and a statement for a bearer instance of its own with no path back. This extension adds two authorities a node's operator gives in its key log — the rolecache/serveand the class authoritycache-transmission(§4) — and one claim, a jwp-receipts claim of class cache-transmission (jwp-receipts §2.4.18, §5 here), by which a node declares amulticastorunidirectionalinstance of its own and the objects of a group it sent over it, with a root under the publisher's tree checked without the bytes. A verifier places a server key by the key logs its relying party names, in jwp-receipts' three levels (§4.4); derives the bearer verdict with the caches' instances beside the publisher's, crediting a cache per instance and only for what it says it sent (§7); and counts, of a population of receiver keys stated for a region, how many held an object by a deadline on their own signed word, with a miss rate in parts per million held to a target (§8). WAI defines no transport. The reference implementation is thecache_authoritymodule ofwai-rs(featurecache_authority), with the class's body inclaim_receipt::bearer; the corpus islive-conformance/cache-authority/, and its second implementation islive-conformance/cache_authority_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.Texts read for this revision:
draft-ietf-moq-transport-23(MOQT) §2.1, §7.1 and §7.9; RFC 9421 §2.3, §2.5, §3.3.6, §4.3 and §7.2.8; RFC 9211 §2, §2.1, §2.2, §2.5 and §6; RFC 7574 §3.2, §9.2 and §11.2.4; RFC 8006 §4.3.8 and RFC 9388 §2.1.1.1 and §4.1. A clause number of one of them in this text is that text's.
1. Scope
In scope: the authorities a node that serves from what it holds signs under, who gives them, and how a verifier places a server key (§4); the cache-transmission claim (§5); what a receiver of a peer-served object does (§6); the bearer verdict with a cache's instances (§7); and the count of a stated population's receivers that held an object by a deadline (§8).
Out of scope: a transport; how a node chooses what to keep, or an asker whom to fetch from, which are holdings §5.4's; a record of service beside settlement's acknowledgement and declaration (settlement §3, §4), which this extension does not add; where a receiver is, which is its own state (§8.1); a price, which is rung-price's; and which settlement object class carries an object a node serves on request from what it holds outside a room (§9).
Relationship to the core spec: wai.delivery.cache is a record (SPEC §5): the
cache-transmission claim is a signed record a WAI tool emits and verifies. It names no payload
format and is not dispatchable.
2. Terms
- Group, link hash, leaf count, content hash: as jwp-receipts §2.1, §2.4.1 and §3 step 3. Object i of a group is its object at position i, from 0.
- Publisher: the key that signed a group. Node: a party that serves, from what it holds, objects of groups another key signed — a neighbourhood cache, a peer, a home gateway.
- Bearer, bearer class, bearer instance: as bearer-receipts §2. A cache instance is a bearer instance a node declares as its own (§5).
- Head: the head revision of an origin's key log (jwp-receipts §7.2), as a JSON text, keyed by the origin; held: passing jwp-receipts §7.2 rules 1 to 3 for that origin.
- Instant: a count of milliseconds since 1970-01-01T00:00:00Z, leap seconds not counted, as
holdings §2 writes one. Last instant: 253,402,300,799,999
(9999-12-31T23:59:59.999Z), as timeliness §2 bounds a deadline. A settlement
window is in microseconds and excludes its
end(settlement §2). - Held-at statement: timeliness §2's: a key, a group, a position and an instant, the key's signed word that it held that object no later than that instant, and its rule for a statement before its group.
- Region: a region code of rung-price §2. ppm: parts per million.
- Bitmap: object i is bit
0x80 >> (i mod 8)of byte ⌊i / 8⌋, as a reconciliation'sreceivedis (bearer-receipts §5.1).
3. Why two authorities and one claim
- No new record of service. "These bytes, I served them" exists: a settlement declaration is the server's word, and an acknowledgement the receiver's countersigned word, with byte ranges and a signed window. Nothing here adds a third.
- Two authorities, as relays already hold them. A relay holds a role authority (
live/relay, live-metering §5.2, settlement §13) and, to sign adelivery-gapclaim, that class's own (jwp-receipts §7.5 Stage and delivery gap). A node does the same: the rolecache/serveplaces it as a server, and the class authoritycache-transmissionlets it sign the one claim this extension registers. - One claim, for an instance with no path back. Over a two-way link the receiver acknowledges
what it received. Over a node's own
multicastorunidirectionalinstance it can only reconcile later (bearer-receipts §5), and a reconciliation over an instance nobody declared is counted nowhere (bearer-receipts §6.2 step 5). jwp-receipts §4.3 requires relay attestation to be "carried as a separate, parallel receipt", and §7.5's last item notes it has no class: the cache-transmission claim is that receipt. - A statement about one group, as a claim. It is about one group, travels with its receipts
on the same bearer, and is the counterpart of
transmissionandreconciliation, so it is a jwp-receipts claim under the claim domain, chained per signer (jwp-receipts §2.4.3), not a binary record.
4. Authorities
4.1 What cache/serve lets a key do
A key that holds cache/serve acts as a node that serves, from what it holds, objects of groups
another key signed. Under it the key signs its holdings records (holdings §4) and its
declarations of what it served (settlement §4), and is the upstream of the acknowledgements its
receivers sign (settlement §3). A key that also holds cache-transmission signs
cache-transmission claims (§5), each declaring a bearer instance of its own and the objects of one
group it sent over it. That is all either covers.
4.2 What neither lets a key claim
- Authorship: one signer per group (jwp-receipts §4.3). Neither is
delivery, and a key with them alone confirms no group receipt (jwp-receipts §7.4 step 6,authority_not_granted). - Transformation: other bytes are a new object (http-delivery §9;
deriving-edge §3.2), which needs
edge/derive. - The publisher's statements and rights: every other class keeps its own authority (jwp-receipts §7.5), and a node forwards those statements unaltered (jwp-receipts §4.1).
- The publisher's transmission: a
transmissionclaim is signed by the group's key (bearer-receipts §4.3 step 2). A node declares only an instance of its own (§5), never the publisher's (§7.2 step 4), and a cache instance never makes a grouptransmitted(§7.3). - A room's hop: neither is
live/relay; a per-hop session record keeps its role (live-metering §5.2).
A node's class under jwp-receipts §4 is set by what it does, not by these authorities: a §4.1 relay when it reads the receipts it forwards (jwp-receipts §4.5), an HTTP cache under jwp-receipts §4.5 when it stores and serves byte for byte. jwp-receipts §4.3 binds it either way.
4.3 Who gives them
The node's operator's origin lists, in its key log, the nodes it runs with cache/serve, and
with cache-transmission those that declare instances of their own, as it lists its relays with
live/relay and delivery-gap:
- Integrity needs no grant. A node changes no byte, so jwp-receipts §3 verifies what it serves
against the publisher's receipts, whoever it is.
edge/derivecomes from the source's origin (deriving-edge §8) only because an edge publishes new bytes under the source's content. - The payer places the server. Settlement §13 pays on keys "the hop's origin lists with the authority of its role".
- It scales without the publisher. A neighbourhood node holds the groups of every publisher on a broadcast; making each publisher list every node would make the content's owner run the carrier's fleet.
A publisher that runs its own nodes lists them under its own origin, which is then the operator's.
An origin SHOULD NOT give cache/serve or cache-transmission to a key it also gives
delivery: such a key's sending of its own groups is the publisher's (bearer-receipts §4), and its
cache-transmission claim over them is refused (cache_transmission_by_publisher, §5.3 step 2).
Neither authority is pinned-only: nothing a sink presents or obeys depends on it.
4.4 Placing a server key
Inputs. A 32-byte Ed25519 key K; an instant at_ms, the time of the item the key is placed
for; and the heads the relying party names, each a JSON text keyed by its origin (as deriving-edge
§7.1 holds them). A JSON object cannot hold one origin twice (jwp-receipts §3.1 step 1), so there is
one head per origin. No randomness and no clock.
Serving roles, in this order: cache/serve, live/relay, live/compositor, live/ingest
(the last three the roles settlement §13 places).
Steps. Heads are read in increasing byte order of their origins (jwp-receipts §7.1); "least"
below is first in that order. A retired entry's until is read as seconds since 1970, and
at_ms is later than it when at_ms > until × 1000, as jwp-receipts §7.4 step 5 compares
times. The placement is the first of these that holds:
- refused
key_revoked, with the least such origin and the entry'scompromised_since: a held head has an entry whosekeyis K and whosestatusisrevoked, whatever itsmay_sign. A revocation attests that another party may hold the key (jwp-receipts §7.2), so no role outlives it. - unconfirmed
key_log_not_held, with the least such origin: a head is not held. It might revoke K, so nothing is confirmed while it cannot be read. - confirmed, with the least such origin, the entry's
id, the first serving role in the order above that itsmay_signholds and, for a retired entry, itsuntil: a held head has an entry whosekeyis K, whosemay_signholds a serving role, and which isactive, orretiredwithat_msnot later than itsuntil. - refused
key_retired, with the least such origin and itsuntil: a held head has an entry whosekeyis K and whosemay_signholds a serving role, and every such entry is retired withat_mslater than itsuntil. - refused
authority_not_granted, with the least such origin: a held head lists K with no serving role. - unconfirmed
server_not_listed: no head lists K, including when no head is given.
A placement is written confirmed origin=… key_id=… role=…, followed by until=… for a retired
entry; unconfirmed reason=server_not_listed; unconfirmed reason=key_log_not_held origin=…;
refused reason=key_revoked origin=… compromised_since=…;
refused reason=key_retired origin=… until=…; or refused reason=authority_not_granted origin=….
The levels are jwp-receipts §7's, never reported as one another. A key that claims no origin
contradicts nothing by being absent from a head, so step 6 is unconfirmed; a head that lists the
key and withholds the role, or revokes or retires it, speaks against it, so steps 1, 4 and 5 are
refused. A placement is not jwp-receipts §7.4's confirmation: it uses the heads the relying party
names, held under rules 1 to 3, with no anchor (jwp-receipts §7.3), as deriving-edge §8 checks
edge/derive. A cache-transmission claim's own confirmation of its signer_id (with
cache-transmission) is reported beside it, as for any claim; neither changes the other.
Which instant. For a cache instance (§7.2 step 4), the latest instant any of its claims'
issued_at allows (§8.3). For a server in a tally (§8.4), the latest on-time evidence instant that
credits it. For any other item, its own signed time.
A placement never changes a verdict (§7) or a hit (§8). It decides only how a server is reported, and which keys a payer may honour (settlement §13). A placement is never an error: no head makes a verdict or a tally fail.
4.5 The holdings key and the service key
Holdings §9 lets a node sign each record with a fresh key, for privacy; settlement §13 pays only
keys it can place. A node whose service is to be placed signs its holdings records, its
declarations and its claims with the key its operator lists. An asker names, as upstream, the
key it found the object under (holdings §5.4), and a payer places that key. A node that signs with
fresh keys has chosen not to be linked, and privacy wins where the holder chooses it: its askers
still verify what it serves (§6), its service still counts as hits (§8), and it is reported
unconfirmed server_not_listed, paid as no one's.
5. The cache-transmission claim (class: "cache-transmission")
A node that sent, from what it held, objects of a group another key signed, over a multicast or
unidirectional bearer instance of its own, MAY declare it in a cache-transmission claim,
one per instance and group, after it has sent what it declares. Its subject_type is receipt
and its subject the group receipt's link hash (jwp-receipts §3 step 3). It is signed by the
node's key, never the group's. signer_id is the node's operator's origin-bound did:web where
its log lists the key, or its did:key otherwise; the second form is unconfirmed under
jwp-receipts §7.4 step 1 and still placeable by §4.4, which matches the key's bytes.
5.1 Body
| member | type | meaning |
|---|---|---|
bearer |
string | The instance's class, one of bearer-receipts §2's; another string is field_type at jwp-receipts §3.1 step 2. |
bearer_id |
absolute URI | The node's own instance. WAI reads nothing from it and compares it byte for byte. |
leaf_count |
integer | The group's leaf count. |
sent |
hex | A bitmap of ⌈leaf_count / 8⌉ bytes. The bit of object i is set exactly when the node sent object i over the instance, from what it held, as the bytes the group's receipts commit to. |
sent_root |
32 B hex | jwp-receipts §2.2's root over the leaves of the sent objects, in increasing position, each leaf in the form its object receipt takes (legacy or labelled). With every bit set it is the group's root_hash. |
energy |
object or null |
As bearer-receipts §4.1 energy: what the instance spent sending these objects, every repetition included. null when not metered, never a figure of 0. |
attests |
string | MUST be "transmitted". |
One meaning has one form: the bitmap sets at least one bit and none at or past leaf_count
(§5.2 rule 3), hex is lowercase (jwp-receipts §2.4.3), and energy is null or an object, never
absent.
{"body":{"attests":"transmitted","bearer":"multicast","bearer_id":"https://bearer.example/mesh-c1",
"energy":null,"leaf_count":4,"sent":"50","sent_root":"<32 B hex>"},"class":"cache-transmission",
"issued_at":"2026-11-03T11:00:05Z","kind":"claim","parent_receipt_hash":null,"seq":0,
"sig":"<64 B hex>","signer_id":"did:web:operator.example#c1","signer_pubkey":"<32 B hex>",
"subject":"<group link hash>","subject_type":"receipt"}
("50" holds objects 1 and 3 of four.)
5.2 Rules
At jwp-receipts §3.1 step 4, in order:
attestsis"transmitted": otherwisecache_transmission_not_transmitted. A declaration that says it attests more is refused, never read as a weaker one.beareris notbidirectional: otherwisecache_transmission_on_bidirectional. Over a path back the receiver acknowledges what it received (settlement §3), and a second statement of the same service would count it twice.leaf_countis at least 1,sentholds exactly ⌈leaf_count/ 8⌉ bytes, no bit at or pastleaf_countis set, and at least one bit is set: otherwisecache_transmission_bitmap_malformed. A node that sent nothing has nothing to declare.- Where
energyis notnull: bearer-receipts §4.2 rule 2 (energy_zero_figure,energy_unknown_acquisition,energy_legacy_acquisition,energy_acquisition_evidence).
5.3 Against the group
At jwp-receipts §3.1 step 6, against the group receipt the verifier holds, which passes
jwp-receipts §3 steps 1 and 2 (context_invalid otherwise; a receipt of another kind is
context_mismatch), and the object receipts it holds under that group, each of which passes
jwp-receipts §3 step 5 (inclusion at least), in order:
- the group's link hash is
subject: otherwisecache_transmission_subject_mismatch; signer_pubkeyis not the group's: otherwisecache_transmission_by_publisher. A publisher's own sending is atransmission(bearer-receipts §4);leaf_countis the group's: otherwisecache_transmission_leaf_count_mismatch;- with every bit set,
sent_rootis the group'sroot_hash. Otherwise the verifier holds an object receipt at every set position (cache_transmission_objects_not_held, recoverable by obtaining them, never accepted silently), andsent_rootis the root over their leaves (cache_transmission_root_mismatch). A root over a leaf that is not the group's at its position is refused here: the digests served are checked against a group the node did not author, without the bytes.
A claim checked without its group stops at step 5 and is counted nowhere (§7).
5.4 What it attests
The node says it sent those objects, from what it held, over its instance. It does not attest that any receiver received them (a reconciliation's, bearer-receipts §5), that it authored or changed them, or that it had any right of the publisher's to send them. A transmission is not a delivery, on any bearer (bearer-receipts §2).
Each of a node's statements binds to the group by hash and none re-signs or re-roots anything
(jwp-receipts §4.3): a cache-transmission claim by subject and by sent_root under the group's
root; a holdings record by its entry's group; a settlement record by its items' content hashes.
A node's claims chain per signer through parent_receipt_hash and seq (jwp-receipts §2.4.3),
across every group it declares, so a verifier is given its earlier claims or reports
parent_not_held. There is no hop bound: a peer served by a peer is two services, each checked
against the publisher's receipts at its own door (jwp-receipts §4.1).
5.5 Authority
Confirmed under jwp-receipts §7, the claim needs cache-transmission, which is neither pinned-only
nor delivery. Where energy is not null, its figure is a second item that needs
cache-transmission and energy/<class>, as a transmission's does (bearer-receipts §4.4). The
claim's signer is placed as a server by §4.4, reported beside.
6. A peer-served object at the receiver
- The verdict is unchanged. The receiver verifies the object under its object receipt and the
group receipt (jwp-receipts §3):
delivered-verifiedover the bytes it received, whoever sent them (holdings §6). - Placement is not evidence. A receiver MUST NOT treat a server's placement (§4.4) or its claims as evidence of the bytes. It MAY report the placement beside the verdict, never changing it.
- What counts the service. With a path back, the receiver acknowledges the server (settlement
§3,
upstreamthe key it fetched from). With none, it reconciles over the server's instance (bearer-receipts §5,bearer_idthe node's). Either is what §8 counts. - Learning the server's key. From the server's holdings record, or over HTTP from a response signature (§10). An object whose bytes fail is refused as jwp-receipts §3 refuses it and is never acknowledged (settlement §10).
7. The verdict
7.1 Inputs
Bearer-receipts §6.1's — the group receipt, the object receipts with envelopes where held, the
claims in each signer's chain order, optionally a sink's own class and instance, a statement
offered and count_only — with cache-transmission claims among the claims, and the heads of §4.4.
A verifier that implements bearer-receipts and not this extension refuses a cache-transmission
claim (unknown_class before this registration, context_mismatch at bearer-receipts §6.2 step 3
after it) and sets aside a reconciliation over a cache instance as bearer_not_declared: the
unknown class is reported, as jwp-receipts §2.4.4 requires, never counted.
7.2 Steps
-
The group and its objects: bearer-receipts §6.2 steps 1 and 2. A group that fails is refused with its reason, and nothing is derived from it.
-
The claims, in order: jwp-receipts §3.1 steps 1 to 5, then step 6: a transmission claim by bearer-receipts §4.3, a reconciliation by bearer-receipts §5.3, a cache-transmission claim by §5.3 here. A claim of any other class is
context_mismatch. A refused claim takes no further part. -
The publisher's declarations: bearer-receipts §6.2 step 4.
-
The caches' instances: the accepted cache-transmission claims, by
bearer_id. Every claim of an instance is set asidebearer_contradictedwhen:- an accepted transmission claim has that
bearer_id, whether its declarations stand or were set aside at step 3: the instance is the publisher's, and a party that names it as its own contradicts the publisher, whose declarations keep what step 3 gave them; or - two of them differ in signer or in body: one instance is one party's transmission, and a node that changes its account of it has stated neither. Such an instance is contradicted.
Two with the same signer and body are one, whatever their
issued_atandseq. Otherwise the instance stands; its server is the claims' signer, placed by §4.4. - an accepted transmission claim has that
-
The reconciliations, each accepted one in order, are counted as bearer-receipts §6.2 step 5 counts them, over the publisher's declarations and the caches' instances that stand. One is set aside, and counted nowhere, at the first of these that holds:
- step 3 set aside the declarations of its
bearer_id, or itsbearer_idis a contradicted cache instance (bearer_contradicted); - neither a declaration nor a cache instance that stands has its
bearer_id(bearer_not_declared); - that declaration or instance names another
bearer(bearer_contradicted); - it is over a cache instance and signed by its server (
self_acknowledged, settlement §3.6): a party's statement that it received what it sent; count_onlyis given and its signer is not among it (sink_not_confirmed).
Otherwise it is counted for its instance. A second counted reconciliation of one key for one instance adds no sink (
reconciliation_duplicate_sink); its bits are joined to the first's. - step 3 set aside the declarations of its
-
The counts:
- bearer-receipts §6.2 step 6 over every counted reconciliation, of either kind of instance:
sinks,wholeandreceived_by, each key's bits joined across all instances; - for each publisher's declaration that stands, its
sinks, as bearer-receipts gives them; - for each cache instance that stands, in increasing byte order of
bearer_id: itsbearer_id,bearer, server, placement and the objects it sent; itssinks, the distinct keys counted for it; and, for each object it sent, the distinct keys whose bits over this instance's counted reconciliations alone hold it. A cache is never credited for an object it does not say it sent, nor for one a key received over another instance.
- bearer-receipts §6.2 step 6 over every counted reconciliation, of either kind of instance:
-
Energy per receiver (§7.4).
A claim's label is bearer-receipts §6.2's (declared, counted,
joined reason=reconciliation_duplicate_sink, set-aside reason=…,
reject step=N reason=…), and cache-declared for a cache-transmission claim whose instance
stands.
7.3 Status, statements and a sink's own instance
- Status is bearer-receipts §6.3's over the publisher's declarations and every counted
reconciliation:
reconciledwhen a reconciliation is counted for any instance (the sinks' word); otherwisetransmittedwhen a publisher's declaration stands; otherwisepublished. A cache instance that stands never makes a grouptransmitted, whose meaning is that the publisher sent it over the bearers declared; the instances are reported beside the status. - Statements (bearer-receipts §6.4): "transmitted over a bearer class" holds only on a
publisher's declaration of that class that stands; "delivered to k sinks" reads
whole, as there. - A sink's own instance (bearer-receipts §6.5):
bearer_contradictedwhen its instance's declarations were set aside or it is a contradicted cache instance; where a publisher's declaration stands for it,declaredwhen the classes agree andbearer_contradictedwhen they do not; where a cache instance stands for it,cache-declaredwhen the classes agree andbearer_contradictedwhen they do not; otherwisebearer_not_declared.
Never delivered on the publisher's word (bearer-receipts §6.3) holds unchanged, and never delivered on a node's.
7.4 Energy per receiver
Bearer-receipts §7 for each cache instance that stands whose energy is not null:
⌈joules_micro / max(1, n)⌉, n its counted keys. It is an upper bound, stated with the
figure's class, and never added to another instance's or class's. A null figure gives no bound,
never 0.
8. Held by a deadline
8.1 The population
The relying party states a population: a region and the receiver keys it counts there. The
keys are the bearer verdict's count_only set (bearer-receipts §6.1): the tally runs the verdict
with count_only the population's keys, replacing any given.
- Region: a region code of rung-price §2, in its capitals, checked by its grammar alone, so
ISO's user-assigned codes pass. This text restates neither the grammar nor its CDNI case mapping.
A price entry (rung-price §4.2) applies to a population when its
footprintholds the region (rung-price §2 Holds). - Keys: 1 to 1,048,576 Ed25519 keys, in strictly increasing order of their bytes: one canonical form, no key twice.
- Population digest: SHA-256 over
wai:cache-population(ASCII) and the byte 0x01; one byte, the region's length, then the region (ASCII); a four-byte big-endian n, then the n keys in order. Region and keys are both inside it, so a population cannot be relabelled.
The population is the relying party's word. Nothing here places a key in a region: a receiver's location is its own state, and "a sink can lie about its own state" (unidirectional-delivery §2). A key is not a receiver (bearer-receipts §7).
8.2 Inputs and checks
The verdict's inputs (§7.1), and: the acknowledgements, each a WDA1 record's bytes with the
item list of each of its entries; the holdings records, each a WHD1 record's bytes (holdings
§4); the object's position i; the deadline; the population; and miss_target_ppm.
The deadline is either deadline_ms, an instant the relying party states, or a timeliness claim
(its text) whose entry for object i stands (timeliness §4, §6.2 steps 2 and 3, against the same
group): deadline_ms is then that entry's by_ms, and the tally reports the claim's link hash
beside it.
The tally is refused at the first of these that holds, and derives nothing:
- the population breaks §8.1:
population_invalid; - a stated
deadline_msis past the last instant, ormiss_target_ppmis above 999,999:target_invalid; - the group fails: its reason (§7.2 step 1); or its
group_startis not an RFC 3339 date-time (jwp-receipts §2.4.3):context_invalid, as carousel §4.3 refuses a schedule's group; - i is at or past the group's leaf count:
object_out_of_range(holdings §7); - the verifier does not hold object i
delivered-verified(§7.2 step 1, with its envelope):object_not_verified. Object receipts carry no length (jwp-receipts §2.4.2), and without the envelope's length ℓ a whole object cannot be told from part of one. The envelope gives ℓ and the content hash h; - the deadline is a timeliness claim that is refused at jwp-receipts §3.1 steps 1 to 6 against
this group, the object receipts the verifier holds under it and their envelopes where it holds
them (timeliness §4.3, its steps 6 and 8 among them), with no claim of its signer held before
it, or that has no entry for i:
deadline_not_stated(timeliness §9). An object with no deadline is never on time.
A head that is not held refuses nothing: it leaves placements unconfirmed (§4.4 step 2).
8.3 Evidence
Each piece of evidence is a held-at statement of timeliness §2, whose instant is the latest the signed word allows, in milliseconds, rounded up where the word is finer. This text adds two kinds to the one timeliness derives from holdings records, and restates neither the form nor the rule:
- a counted reconciliation's instant is its
issued_at, read as jwp-receipts §2.4.3 reads it (offset applied, second 60 read as the next minute's first), rounded up to the millisecond: 1000 × those seconds + ⌈f × 1000⌉, with f the written fraction as a decimal (so…:00.000001Zadds 1 and…:00.2Zadds 200). Its whole seconds are those seconds, the fraction dropped; - an acknowledgement's instant is its window's last microsecond,
end− 1, since a window'sendis excluded (settlement §2), rounded up to the millisecond: ⌈(end− 1) / 1000⌉. Its whole seconds are ⌊(end− 1) / 1,000,000⌋. The window'sstartis never read.
Before the group. By timeliness §2's rule, a statement whose whole seconds are below the seconds
jwp-receipts §2.4.3 reads from the group's group_start is before its group, and is set aside
held_before_group: it gives no evidence. For an acknowledgement, that is an end at or below the
group's start in microseconds. A receiver whose clock was never set signs instants near 1970, and
they are never on time.
Only a population key's evidence is read. For each population key k:
-
Reconciliation evidence. Each reconciliation counted at §7.2 step 5 (its label
countedorjoined) whose signer is k and whose bit i is set: a held-at statement at its instant, set aside when before the group. Its server is its instance's sender when it sent i: the group's key for a publisher's declaration, the cache's server when itssentholds i; none otherwise. A reconciliation attests no timeliness (bearer-receipts §5.4); what is read is its signer's signed word that it held the objects when it signed the claim, and a receiver with no path back signs at reception and sends the claim later. -
Acknowledgement evidence. Each acknowledgement given is read under settlement §3.6 (its codes, among them
truncated,malformed,unknown_version,ack_invalid,self_acknowledged,bad_keyandbad_signature); a declaration (mode2) is its server's own word and never evidence (settlement §4.2); a record ofmode1 must be given one item list for each of its entries (record_missing), each reading and recomputing against its entry (items_invalid,items_mismatch). Each is labelled, in order:reject reason=…,declaration,before-groupwhen it reads and its last microsecond is before the group, andreadotherwise. A record that does not read gives no evidence. Items of every object class count: the class is settlement's, and the tally reads only content hashes and ranges. Then, of the records that read, withdownstreamk:- take those that hold an item of h, setting aside those before the group, and order the rest
by
end, then by digest (settlement §3.5); - the first record at which the union of h's byte ranges, in it and in the records before it, covers every byte from 0 to ℓ − 1 gives the evidence: a held-at statement at that record's last microsecond;
- its servers are the
upstreams of the records up to and including that one. Parts from several peers, as a swarm delivers them, complete an object together, and each peer that served part of it is credited.
Acknowledgement chains (settlement §3.7) are settlement's and are not checked here; each record is read as its signer's word about its own window.
- take those that hold an item of h, setting aside those before the group, and order the rest
by
-
Holdings evidence. Each holdings record given is read (holdings §4.4) and held to the group as timeliness §6.2 step 4 holds it: refused
holdings_leaf_count_mismatchwhen its entry for the group states another leaf count, andheld_before_groupwhen it has an entry for the group and itsas_of_msis before the group; of one holder's others, two with oneas_of_msand different digests set both aside (holdings_contradicted). Each is labelled as timeliness labels a record. A record of k that stands and holds object i (holdings §5.4) is a held-at statement at itsas_of_ms(timeliness §6.2 step 5). It names no server.
k is a hit when some held-at statement of k for object i has an instant at or before
deadline_ms, and a miss otherwise. For an acknowledgement, that is end − 1 ≤
deadline_ms × 1000, with no overflow in 64 bits. A line of §9 for object i and a hit read the
same statements, so a key on_time in such a line by by_ms is a hit at that deadline in a tally
of object i whose population holds it.
8.4 The count
- n is the population's size;
hits;misses= n −hits. miss_ppm= ⌈misses× 1,000,000 / n⌉, rounded up so the reported rate is never below the true one.missing: the keys that missed, in population order: the list an operator repairs before a deadline, or reports after it.set_aside: each held-at statement of a population key set asideheld_before_group, as its key, its source (reconciliation, or an acknowledgement's digest in lowercase hex) and the reason, in increasing order of key, then source.- Servers. For a hit k, S(k) is the set of servers of its on-time evidence, and U(k)
holds when some on-time evidence of k credits no server. For each key in the union of the
S(k), in increasing byte order:
- its placement:
publisherfor the group's key, otherwise §4.4's at the latest on-time evidence instant that credits it; hits= the number of hits k with the server in S(k);sole= the number of hits k with S(k) = {server} and not U(k): the hits it alone served.
- its placement:
unattributed= the number of hits k with S(k) empty.
8.5 Outcome
Met when miss_ppm ≤ miss_target_ppm; otherwise refused miss_target_exceeded. Reported
either way: the region, the population digest, deadline_ms and a timeliness claim's link hash,
n, hits, misses, miss_ppm, missing, set_aside, the servers, unattributed, each
acknowledgement's label, each holdings record's label, and each claim's label under §7.2.
A met tally says that, of the stated keys, the share given held the object, by their own signed
word, by the deadline. It does not say that they presented it, that the population is everyone in
the region, that any key is one receiver, or that the population was chosen before the deadline
(§13). It is not carousel §6.4's "held by t", which is the publisher's plan for a receiver within
a loss bound; a relying party may hold both. The asker's plan of whom to fetch from stays holdings
§5.4's, and the operator's plan of whom to repair is missing.
9. Composition
-
holdings: a holder signs with the key its operator lists to be placed (§4.5; holdings §9). A record is the third kind of held-at statement §8.3 reads.
-
carousel: availability (carousel §6.3) is the publisher's plan for a receiver within a loss bound; a node repairs what that plan did not cover, and §8 counts the outcome. A cache instance has no schedule: a schedule is signed by the group's key (carousel §4.3 step 2), and a cache-transmission claim and a schedule share no instance (§7.2 step 4).
-
bearer-receipts: the verdict extends its §6.2 here, as carousel's does. Its §7 bound applies to each cache instance, and its §8 holds for a node's key: a receiver with no path back places a node only by heads provisioned in advance, and a key log arriving over the node's instance anchors nothing (
anchor_on_bearer). -
settlement: no new object class and no figure changes. Settlement §13 places a node that serves from what it holds by
cache/servefrom its operator's origin. A node MAY state a one-way re-sending's bytes in a declaration (settlement §4, zerodownstream) beside its cache-transmission claim; a declaration bills nothing (settlement §4.2), and no tariff prices a cache instance, so a node's one-way re-sending is counted, credited and placed here and billed nowhere. Which settlement object class carries an object a node serves on request from what it holds outside a room, and on whose word a payer honours a sink's acknowledgement outside a room, settlement §3.3 and §13 do not say, and this text does not decide. -
takedown: a cache-transmission claim is its signer's own word that it held what it sent, as a declaration is (takedown §7.1,
served). -
jwp-receipts §4: the claim is the parallel receipt jwp-receipts §4.3 permits, and §4.2 lets a relay, or a node that is not one, sign it.
-
timeliness: it owns the per-object deadline and the held-at statement, which this text extends with acknowledgements and counted reconciliations (§8.3). Timeliness §6's verdict and the statement it answers (its §6.4) read holdings records alone. A verifier that holds this text's inputs (§7.1, §8.2) and a timeliness claim MAY derive the lines of the claim's entry for object i by timeliness §6.3 from held-at statements of all three kinds:
- the holdings records' exactly as timeliness §6.2 step 5 derives them, for the primary and its fallback whatever envelopes the verifier holds, with a line for each holder with a record that stands and has an entry for the group or the fallback's group;
- those §8.3 derives from acknowledgements and counted reconciliations, for every object of the
group the verifier holds
delivered-verified, the primary and a fallback in the group among them, with a line for each key with one. A fallback in another group is held by records alone, since this text judges one group.
The claim is checked as §8.2 step 6 checks a deadline's, and a claim refused there, or with no entry for i, derives no lines (
deadline_not_stated). A fallback rendition is another object with its own content hash, so it has its own tally and is never a hit for the primary. -
rung-price: it owns price, direction and a price list's
footprint, in the region codes of its §2, which §8.1 uses. It carries no deadline and no miss target, so the tally takes the deadline, the target and the population as stated inputs and reports the population's digest (and a timeliness claim's link hash) so that a contract can name them. Joules are stated, never priced. A comparer MAY accept a seller whose key it places with a serving role (§4.4). -
http-delivery: a cache-transmission claim listed in a receipt index is checked at its §8.2 step 6 against the group of an item whose link hash is its subject and the object receipts of the items listed under it.
-
live-rooms: inside a room a payer may take the grants' principals as the population; §8.1 is its analogue outside one.
10. Mappings
| standard | binding |
|---|---|
MOQT, draft-ietf-moq-transport-23 §7.1 (Caching Relays: "Relays MAY cache Objects, but are not required to"), §7.9 (Relay Object Handling: a relay "MUST treat the object payload as opaque" and "MUST NOT combine, split, or otherwise modify object payloads"), §2.1 ("The Original Publisher is solely responsible for the content of the object payload") |
A node with cache/serve serving over MoQ is a §7.1 caching relay held to §7.9 and to jwp-receipts §4.1 and §4.3. |
HTTP Message Signatures, RFC 9421 §3.3.6 (ed25519), §2.3 (keyid, tag), §2.5, §4.3 ("Multiple distinct signatures MAY be included"), §7.2.8 |
A node serving over HTTP MAY sign each response with alg="ed25519", keyid its key's did:key (jwp-receipts §7.1) and tag="wai-cache", covering content-digest (RFC 9530 §2). A downstream that verifies signature and digest learns the key it names as upstream. An RFC 9421 signature base begins with " (§2.5), and every WAI signing input begins with its ASCII domain, so one key's two kinds of signature never sign the same bytes. |
Cache-Status, RFC 9211 §2, §2.1 (hit), §2.2 (fwd), §2.5 (stored) |
A member with hit is HTTP's own unsigned "served from what it held" (settlement §9.1); fwd with stored is a fill. RFC 9211 defines no signature, so its identifier is not a key and places nothing. |
CTA-5006 CMSD-Dynamic, as delivery-telemetry §6, its paragraph 6.3, maps it |
A node MAY write its did:key as its entry's server string; a player that reads it learns a key it may name as upstream, unsigned, and SHOULD prefer an RFC 9421 signature where one is present. |
PPSPP, RFC 7574 §3.2 (HAVE), §5, §9.2 ("reciprocity enforcement is the sole responsibility of the sending peer"), §11.2.4 |
Informative: a holdings record is close to HAVE, and jwp-receipts §3 to PPSPP's content integrity. PPSPP leaves reciprocity and accounting local; this extension gives their portable form. |
| DVB-NIP, ETSI TS 103 876 §6.2.2 and §6.5 | A gateway that stores broadcast assets and serves them to home clients is a node: its key holds cache/serve, its holdings record advertises, its clients acknowledge it. |
| CDNI footprints, RFC 8006 §4.3.8 and RFC 9388 §2.1.1.1 | Through rung-price §2, which binds them: a region is ISO's capital form. |
11. Reasons
The codes this extension adds, each with where it is raised:
| reason | raised at |
|---|---|
server_not_listed |
§4.4 |
cache_transmission_not_transmitted |
§5.2 rule 1 |
cache_transmission_on_bidirectional |
§5.2 rule 2 |
cache_transmission_bitmap_malformed |
§5.2 rule 3 |
cache_transmission_subject_mismatch |
§5.3 step 1 |
cache_transmission_by_publisher |
§5.3 step 2 |
cache_transmission_leaf_count_mismatch |
§5.3 step 3 |
cache_transmission_objects_not_held |
§5.3 step 4 |
cache_transmission_root_mismatch |
§5.3 step 4 |
population_invalid |
§8.2 |
target_invalid |
§8.2 |
object_not_verified |
§8.2 |
miss_target_exceeded |
§8.5 |
The extension also raises key_revoked, key_retired and authority_not_granted (jwp-receipts
§7.7), at the levels §4.4 gives them, and key_log_not_held (deriving-edge §9), at §4.4;
bearer_contradicted, bearer_not_declared, sink_not_confirmed,
reconciliation_duplicate_sink, delivery_not_reconciled and delivery_exceeds_reconciled
(bearer-receipts §10) at §7.2 and §7.3; self_acknowledged (settlement §11) at §7.2 step 5;
object_out_of_range (holdings §7), context_invalid (jwp-receipts §3.1) and
deadline_not_stated (timeliness §9) at §8.2; settlement §3.6's codes and record_missing for an
acknowledgement, holdings §7's codes for a holdings record, and held_before_group (timeliness §9)
for any held-at statement, at §8.3; for a group or object the codes of jwp-receipts §3.3; and for a
claim every code of jwp-receipts §3.1 steps 1 to 5, bearer-receipts §4.2 rule 2's energy codes, and
those of step 6 named in §5.3.
12. Conformance
live-conformance/cache-authority/ holds the corpus, written
from fixed seeds by wai_cache_authority_vectors gen as vectors.json. Every key comes from a
published seed of one byte repeated 32 times (jwp-receipts §7.8), both key logs are test logs, and
regions are ISO 3166-1's user-assigned codes. It has:
- Claims, each through jwp-receipts §3.1 steps 1 to 6 against the context it gives: objects sent from a group, every object with no object receipt held, the last object of a group of nine, labelled leaves, a metered instance, a chained second claim; a chain broken and a parent not held; one negative control for each rule of §5.2 and each step of §5.3, an unknown bearer class, an extra member and an energy figure of 0; a context that is not a group receipt; and a claim with no group, which stops at step 5.
- Placements: each step of §4.4, the least origin, a serving role other than
cache/serve, a retired key at itsuntiland a millisecond past it, a revocation beating a confirmation and outliving another role, a head not held, a head under another origin, and a revocation beside a head not held. - Verdicts: a cache instance beside the publisher's with per-instance credit; reconciliations
only over a cache's instance; a cache instance alone, published, with a statement of
transmission refused; an instance taken from the publisher, with the publisher's declarations
standing and contradicted; two caches on one instance; one claim twice and a cache contradicting
itself; each reason of §7.2 step 5; placements of every level beside unchanged counts, and a
retired node's two claims of one instance on either side of its
until; a sink on a cache instance; the energy bound before and after keys are counted, and unmetered; another class; and a group that fails. - Tallies: a target met exactly and missed by one ppm, a rate rounded up; an acknowledgement
ending at the deadline and one and two microseconds past it; a reconciliation signed at the
deadline, a microsecond past it, below a second, with an offset and at second 60; evidence before
the group of each kind, and a reconciliation in the last second before it whose instant rounds up
to its start; an object completed by two peers, in time and late, the late one with its records
given latest first, two records of one
endthat their digests order, and a part never completed; a declaration, a key outside the population, and an acknowledgement refused for each reason §8.3 names; an object of any class; a bit a cache did not send; servers placed at each outcome of §4.4, a retired key before itsuntilamong them, a retired key credited on either side of itsuntil, and the publisher; the target and deadline bounds on both sides; a region of every length rung-price §2 admits, and a hyphen with no subdivision, a subdivision of four characters and a region in lowercase, each refused; a population empty, out of order and holding a key twice; and each refusal of §8.2, with a deadline taken from a timeliness claim, one with no entry for the object, one about another group and one whose fallback's envelope is held and not derived. - Lines (§9): from an acknowledgement and from a counted reconciliation; from a holdings record whose object's envelope is not held; with a fallback in another group that a holder's record alone names; and with a fallback in the group, held by an acknowledgement and a reconciliation.
The population bound of 220 keys is carried by no case; both implementations check it themselves.
A conforming verifier gives every case the outcome, the reason and everything else the corpus
states. wai_cache_authority_vectors verify and the test tests/cache_authority.rs check that the
file regenerates byte for byte, that wai-rs gives every case its outcome, that every reason of §11
appears, and that §11 lists exactly the reference's codes.
live-conformance/cache_authority_verify.py is the second implementation, written from this text:
it shares no code with wai-rs; it verifies groups and objects with the JWP corpus's second
implementation (jwp-conformance/jwp_verify.py), claims through steps 1 to 5 with the claim
corpus's (claim-conformance/claim_verify.py), reconciliations with the bearer corpus's
(live-conformance/bearer_verify.py), acknowledgements with the settlement corpus's
(live-conformance/settlement_verify.py), key logs with the signer corpus's
(signer-conformance/signer_verify.py), held-at statements with the timeliness corpus's
(live-conformance/timeliness_verify.py) and regions with the rung price corpus's
(live-conformance/rung_price_verify.py); it writes §4.4, §5.3, §7 and §8 itself, reads §11 from
this text, mutates copies of the corpus and requires every copy to fail, and drops each of its own
checks in turn and requires the corpus to catch the weakened verifier.
13. Security and privacy considerations
- A node's claim is its word. A false one is evidence against its key.
- A placement is as good as the heads named. It is the relying party's choice of logs, held under jwp-receipts §7.2 rules 1 to 3 only; a head that is not the head of an origin it anchored (jwp-receipts §7.3) is its own say-so. A revocation the relying party has not retrieved is not in its heads (jwp-receipts §7.4), and the report names each head's origin. Placement never vouches for bytes.
- Evidence instants are the receivers' clocks. A receiver can sign any time. The tally sets aside times before the group exists, and otherwise reads each instant as the latest it allows. A population of the relying party's own receivers, whose software it ships, is what makes them worth reading.
- A population chosen after the deadline proves nothing. Its digest proves which keys were counted, not when they were chosen. A relying party that must show the population was fixed first registers a statement naming the digest with a Transparency Service before the deadline (transparency §3); a registration witnesses existence at a time and attests nothing else (transparency §6). This extension checks no such registration.
- Collusion and sybils (settlement §13). Only population keys count; a node cannot count its own
reconciliations (
self_acknowledged) or acknowledge itself (settlement §3.6). - What is disclosed. Reconciling or acknowledging to a node gives up unidirectional-delivery §2's property for the objects named, as bearer-receipts §12 says. The tally shows each population key's evidence to its verifier. A fresh key per record unlinks a holder and unplaces it.
- Bounded work. At most 220 keys; each acknowledgement and reconciliation is read once; the byte ranges are sorted once per key; a placement is a lookup per head.