Some checks failed
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 12s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 22s
wai-audit collector and player integration / does this change touch it (push) Successful in 19s
wai-fleet service / does this change touch it (push) Successful in 21s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 23s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 33s
wai-sink-swift native sink package / does this change touch it (push) Successful in 23s
WAI Studio / does this change touch it (push) Successful in 18s
wai-sink-android native sink library / does this change touch it (push) Successful in 47s
wai-quantum for JavaScript / does this change touch it (push) Successful in 1m0s
wai-app and Create / wai-app · does this change touch it (push) Successful in 1m2s
wai-key-proxy / does this change touch it (push) Successful in 1m2s
WAI operation catalogue / does this change touch it (push) Successful in 1m3s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 1m9s
wai-ledger service / does this change touch it (push) Successful in 1m2s
wai-quantum studio and desktop / does this change touch it (push) Successful in 1m2s
wai-fleet service / service · macos (push) Successful in 55s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Successful in 51s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 1m25s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 1m35s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 1m48s
wai-key-proxy / proxy · linux (push) Successful in 1m41s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 2m0s
wai-ledger service / service · macos (push) Successful in 56s
wai-fleet service / service · linux (push) Successful in 2m32s
wai-ledger service / service · linux (push) Successful in 2m13s
wai-audit collector and player integration / collector · linux (push) Successful in 2m57s
wai-quantum for JavaScript / javascript · linux (push) Successful in 4m8s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (push) Has been cancelled
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been cancelled
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Has been cancelled
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Has been cancelled
wai-quantum energy-landscape jobs / the same bits · macos (push) Has been cancelled
wai-quantum studio and desktop / studio · browser (push) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Has been cancelled
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 5m13s
wai-quantum studio and desktop / desktop app · macos (push) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Has been cancelled
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Has been cancelled
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (push) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (push) Has been cancelled
WAI operation catalogue / the catalogue and every door · linux (push) Has been cancelled
WAI Studio / Studio in WebKit · linux (push) Has been cancelled
wai-quantum studio and desktop / desktop app · linux (push) Has been cancelled
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Has been cancelled
extensions/audience-decision.md registers two route-record documents, each signed in its canonical form under its own domain. `wai.video.audience` (`wai-audience-decision`): a decider's signed decision of whether the sessions in one region may be shown a programme over a span, under the blackouts in force. Each blackout is named by the id a session class carries (http-delivery Appendix H `blackouts`) and lists its regions in rung-price §2's region codes, with the programme shown in its place. A session aggregates its own location before it leaves the session: to the one listed code that holds it, to outside, or to unresolved (no location, or a country of which only subdivisions are listed); the aggregation decides exactly what the location would. A decision whose evidence holds anything else (coordinates, a postal code, a session's identifier) or a region finer than the aggregation gives is refused `audience_location_not_aggregated`; one whose region the blackouts neither list nor hold, `audience_region_not_in_set`. The blackouts applied (every one, unresolved), the verdict (show, withhold, or alternate when all that apply name one programme) and that programme are recomputed; a session class of the programme carries the applied set as its `blackouts` and the region's country as its `geo`, and a verifier given both states the class's hash. `wai.delivery.territory` (`wai-territory`): a publisher's signed statement of where its content may be shown (everywhere, only in some regions, everywhere but some). Its latest statement stands alone; a lapsed or contradicted latest revives none. The territory (within, outside, undetermined, or unstated with its reason) is reported beside a server key's placement, which cache-authority §4.4 computes from its own inputs and no statement changes; a statement that names servers, nodes, origins, roles or may_sign is refused `territory_scopes_serving`. cache-authority §4.3 and §4.4 are unchanged; §4.4 gains a paragraph saying so. The reference is the `audience_decision` module of wai-rs (feature `audience_decision`); the corpus is live-conformance/audience/ (13 aggregations, 67 decisions, 17 classes, 39 statements, 32 territories, 10 placements), and audience_decision_verify.py is its second implementation: 178 cases agree, 21 of 21 mutated copies fail and 34 of 34 weakened verifiers are caught. Twelve faults planted in the reference each fail its tests: the evidence's extra-member check and its finer-region check, rule 6 (a region not the blackouts'), the serving-member check and `may_sign` among its members, a placement refused beside an outside territory, a decision's and a statement's signature verified without the domain, over the text as written and under the other record's domain, an unresolved session read as outside, and a lapsed statement letting an earlier one stand. Co-Authored-By: Claude Opus 5.5 <[email protected]>
809 lines
57 KiB
Markdown
809 lines
57 KiB
Markdown
# 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 role `cache/serve` and the class authority
|
||
> `cache-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 a `multicast`
|
||
> or `unidirectional` instance 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). A node states its plan for its own instance in a [carousel](carousel.md)
|
||
> `schedule` claim signed by its own key (§5.6). WAI defines no transport. The reference implementation is the
|
||
> `cache_authority` module of `wai-rs` (feature `cache_authority`), with the class's body in
|
||
> `claim_receipt::bearer`; the corpus is
|
||
> [`live-conformance/cache-authority/`](../live-conformance/cache-authority/), and its second
|
||
> implementation is `live-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 and a node's schedule of its
|
||
own instance (§5); what a receiver of a peer-served object does (§6); the bearer verdict with a
|
||
cache's instances and their schedules (§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](holdings.md) §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](rung-price.md)'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](../SPEC.md) §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](jwp-receipts.md)
|
||
§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](bearer-receipts.md) §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](holdings.md) §2 writes one. **Last instant**: 253,402,300,799,999
|
||
(9999-12-31T23:59:59.999Z), as [timeliness](timeliness.md) §2 bounds a deadline. A settlement
|
||
window is in microseconds and excludes its `end` ([settlement](settlement.md) §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's
|
||
`received` is (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](live-metering.md) §5.2, settlement §13) and, to sign a `delivery-gap` claim,
|
||
that class's own (jwp-receipts §7.5 *Stage and delivery gap*). A node does the same: the role
|
||
`cache/serve` places it as a server, and the class authority `cache-transmission` lets 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 `multicast` or `unidirectional` instance 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 `transmission` and `reconciliation`, 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, and one that holds `schedule` as well schedules those instances (§5.6). 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](http-delivery.md) §9;
|
||
[deriving-edge](deriving-edge.md) §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 `transmission` claim 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 group `transmitted` (§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`, with
|
||
`cache-transmission` those that declare instances of their own, and with `schedule` those that
|
||
schedule them, 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/derive` comes 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:
|
||
|
||
1. **refused `key_revoked`**, with the least such origin and the entry's `compromised_since`: a
|
||
held head has an entry whose `key` is *K* and whose `status` is `revoked`, whatever its
|
||
`may_sign`. A revocation attests that another party may hold the key (jwp-receipts §7.2), so no
|
||
role outlives it.
|
||
2. **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.
|
||
3. **confirmed**, with the least such origin, the entry's `id`, the first serving role in the order
|
||
above that its `may_sign` holds and, for a retired entry, its `until`: a held head has an entry
|
||
whose `key` is *K*, whose `may_sign` holds a serving role, and which is `active`, or `retired`
|
||
with `at_ms` not later than its `until`.
|
||
4. **refused `key_retired`**, with the least such origin and its `until`: a held head has an entry
|
||
whose `key` is *K* and whose `may_sign` holds a serving role, and every such entry is retired
|
||
with `at_ms` later than its `until`.
|
||
5. **refused `authority_not_granted`**, with the least such origin: a held head lists *K* with no
|
||
serving role.
|
||
6. **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.
|
||
|
||
Where a publisher's content may be shown is reported beside a placement, never in it: a
|
||
publisher's territory statement ([audience-decision](audience-decision.md) §5) names no server,
|
||
origin or role, and the placement of §4.4 is the same with it as without it (audience-decision
|
||
§5.6).
|
||
|
||
### 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.
|
||
|
||
```json
|
||
{"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:
|
||
|
||
1. `attests` is `"transmitted"`: otherwise `cache_transmission_not_transmitted`. A declaration that
|
||
says it attests more is refused, never read as a weaker one.
|
||
2. `bearer` is not `bidirectional`: otherwise `cache_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.
|
||
3. `leaf_count` is at least 1, `sent` holds exactly ⌈`leaf_count` / 8⌉ bytes, no bit at or past
|
||
`leaf_count` is set, and at least one bit is set: otherwise
|
||
`cache_transmission_bitmap_malformed`. A node that sent nothing has nothing to declare.
|
||
4. Where `energy` is not `null`: 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:
|
||
|
||
1. the group's link hash is `subject`: otherwise `cache_transmission_subject_mismatch`;
|
||
2. `signer_pubkey` is not the group's: otherwise `cache_transmission_by_publisher`. A publisher's
|
||
own sending is a `transmission` (bearer-receipts §4);
|
||
3. `leaf_count` is the group's: otherwise `cache_transmission_leaf_count_mismatch`;
|
||
4. with every bit set, `sent_root` is the group's `root_hash`. Otherwise the verifier holds an
|
||
object receipt at every set position (`cache_transmission_objects_not_held`, recoverable by
|
||
obtaining them, never accepted silently), and `sent_root` is 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.
|
||
|
||
### 5.6 A schedule of a cache instance
|
||
|
||
A node that sends over a cache instance to a plan **MAY** state the plan in a `schedule` claim
|
||
(carousel §4), so that a receiver of its instance plans as a receiver of the publisher's does and
|
||
turns its radio off between the passes that add nothing (carousel §7). Its `subject` is the group
|
||
receipt's link hash and its `bearer_id` the instance's, and it is signed by the node's key, never
|
||
the group's. Its body, rules and blocks are carousel §4.1, §4.2 and §5's; a later schedule extends it
|
||
as carousel §4.5 says; and its availability and the statements held to it are carousel §6.3 and
|
||
§6.4's. It attests that the node scheduled the passes it names. It does not attest that they were
|
||
sent, which is its cache-transmission claim's, that any receiver heard them, or that the node had any
|
||
right of the publisher's to send them (§5.4).
|
||
|
||
**Against the group.** At jwp-receipts §3.1 step 6 in the verdict of §7, a schedule signed by the
|
||
group's key is the publisher's and is checked by carousel §4.3. One signed by another key is a
|
||
node's and is checked by carousel §4.3 but for its step 2, raising that text's codes at its other
|
||
steps. Carousel's verdict and timeliness' plan read the publisher's schedules alone.
|
||
|
||
**Where it stands.** At §7.2 step 4, in this order:
|
||
|
||
1. every node's schedule of an instance is set aside `bearer_contradicted` when an accepted
|
||
transmission claim has that `bearer_id`, whether its declarations stand or were set aside, or when
|
||
the instance is a contradicted cache instance: it is not the node's to plan;
|
||
2. every node's schedule of an instance is set aside `bearer_not_declared` when no cache instance
|
||
stands with that `bearer_id`;
|
||
3. a schedule whose signer is not the instance's server is set aside
|
||
`cache_schedule_signer_mismatch`: a node schedules only the instance it declares, and another
|
||
key's schedule of it contradicts nothing;
|
||
4. of the server's schedules, in the order of its chain, when one does not extend the one before it
|
||
(carousel §4.5) every one of them is set aside `schedule_contradicted`, and the instance itself
|
||
stands. A node that withdraws, moves or changes a pass has stated neither plan.
|
||
|
||
Otherwise the last of them **stands**, and each before it with another body is **extended**.
|
||
|
||
A cache-transmission claim says what was sent and comes after it (§5), and a schedule says what will
|
||
be sent and may come before, so either may come first in the node's chain. A schedule may name
|
||
objects the node's claim does not. **It credits nothing**: a cache is credited only for what its
|
||
claim says it sent (§7.2 step 6), and a schedule changes no count, no status and no placement.
|
||
|
||
**Authority.** Confirmed under jwp-receipts §7, the claim needs `schedule` (carousel §4.4), which the
|
||
node's operator gives beside `cache-transmission` (§4.3).
|
||
|
||
## 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-verified` over 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, `upstream` the key it fetched from). With none, it reconciles over the server's instance
|
||
(bearer-receipts §5, `bearer_id` the 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 and schedule claims among the claims, the heads of
|
||
§4.4, and optionally a statement of carousel §6.4 with its loss bound and overhead (§7.3).
|
||
|
||
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
|
||
|
||
1. **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.
|
||
2. **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 schedule by carousel §4.3 or §5.6 here. A claim of any other class is
|
||
`context_mismatch`. A refused claim takes no further part.
|
||
3. **The publisher's declarations**: bearer-receipts §6.2 step 4.
|
||
4. **The caches' instances**: the accepted cache-transmission claims, by `bearer_id`. Every claim of
|
||
an instance is set aside `bearer_contradicted` when:
|
||
- 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_at` and `seq`. Otherwise the
|
||
instance **stands**; its **server** is the claims' signer, placed by §4.4.
|
||
|
||
Then the schedules: those signed by the group's key by carousel §6.2 step 4, over step 3's
|
||
declarations, and a node's by §5.6.
|
||
5. **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 its `bearer_id` is 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_only` is 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.
|
||
6. **The counts**:
|
||
- bearer-receipts §6.2 step 6 over every counted reconciliation, of either kind of instance:
|
||
`sinks`, `whole` and `received_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`: its `bearer_id`,
|
||
`bearer`, server, placement and the objects it sent; its `sinks`, 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.
|
||
7. **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=…`), `cache-declared` for a cache-transmission claim whose instance
|
||
stands, and `stands` or `extended` for a schedule.
|
||
|
||
### 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: `reconciled` when a reconciliation is counted for any instance (the sinks'
|
||
word); otherwise `transmitted` when a **publisher's** declaration stands; otherwise
|
||
`published`. A cache instance that stands never makes a group `transmitted`, 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_contradicted` when its instance's
|
||
declarations were set aside or it is a contradicted cache instance; where a publisher's
|
||
declaration stands for it, `declared` when the classes agree and `bearer_contradicted` when they
|
||
do not; where a cache instance stands for it, `cache-declared` when the classes agree and
|
||
`bearer_contradicted` when they do not; otherwise `bearer_not_declared`.
|
||
- **A statement held to a schedule**: carousel §6.4's, that object *i* or the group is held by *t* on
|
||
an instance, offered with a loss bound *L* and an overhead *h*, is checked against the schedule that
|
||
stands on that instance, the publisher's or a node's. It holds, or it is refused with carousel
|
||
§6.4's reason, `object_not_scheduled` where no schedule stands there, and `loss_bound_invalid` or
|
||
`overhead_invalid` where *L* or *h* is out of range, as carousel §6.2 checks them; only the
|
||
statement is refused. On a node's instance it speaks for the node's schedule, and says no more than
|
||
carousel §6.4 says of the publisher's.
|
||
|
||
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 `footprint` holds 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](unidirectional-delivery.md) §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:
|
||
|
||
1. the population breaks §8.1: `population_invalid`;
|
||
2. a stated `deadline_ms` is past the last instant, or `miss_target_ppm` is above 999,999:
|
||
`target_invalid`;
|
||
3. the group fails: its reason (§7.2 step 1); or its `group_start` is not an RFC 3339 date-time
|
||
(jwp-receipts §2.4.3): `context_invalid`, as [carousel](carousel.md) §4.3 refuses a schedule's
|
||
group;
|
||
4. *i* is at or past the group's leaf count: `object_out_of_range` (holdings §7);
|
||
5. 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*;
|
||
6. 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.000001Z` adds 1 and `…:00.2Z` adds 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's
|
||
`end` is excluded (settlement §2), rounded up to the millisecond: ⌈(`end` − 1) / 1000⌉. Its whole
|
||
seconds are ⌊(`end` − 1) / 1,000,000⌋. The window's `start` is 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 `counted` or
|
||
`joined`) 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 its `sent` holds *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_key` and `bad_signature`); a declaration (`mode` 2) is its server's own
|
||
word and never evidence (settlement §4.2); a record of `mode` 1 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-group` when it reads and its last microsecond is before the group, and
|
||
`read` otherwise. 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, with `downstream` *k*:
|
||
1. 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);
|
||
2. 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;
|
||
3. its **servers** are the `upstream`s 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.
|
||
- **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_mismatch` when its entry for the
|
||
group states another leaf count, and `held_before_group` when it has an entry for the group and
|
||
its `as_of_ms` is before the group; of one holder's others, two with one `as_of_ms` and 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 its
|
||
`as_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 aside `held_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**: `publisher` for 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.
|
||
- `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 node's plan for
|
||
its own instance is a schedule of its own (§5.6), which shares no instance with the publisher's: a
|
||
publisher's schedule stands beside a publisher's declaration, and a node's beside its own
|
||
cache-transmission claim (§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/serve` from its operator's origin. A node **MAY** state a
|
||
one-way re-sending's bytes in a declaration of version 2 naming its cache-transmission claim
|
||
(settlement §4.4); a declaration bills nothing in a statement (settlement §4.2), and a tariff of
|
||
version 7 prices a cache instance at its transmission prices, from the node's claim, its own
|
||
schedule (§5.6) and that declaration ([receipt-ledger](receipt-ledger.md) §4.10). What is counted,
|
||
credited and placed here does not change. 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](takedown.md) §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 each
|
||
rung of 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 a rung'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 rung in the group among
|
||
them, with a line for each key with one. A rung 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 rung is another object with its own
|
||
content hash, so it has its own tally and is never a hit for the primary. A line's form is
|
||
timeliness §6.3's: a ladder written as an array states the least instant of each rung.
|
||
- **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](delivery-telemetry.md) §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 |
|
||
| `cache_schedule_signer_mismatch` | §5.6 |
|
||
| `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; a schedule's codes of carousel §4.2 and §4.3 at jwp-receipts
|
||
§3.1 steps 4 and 6, `schedule_contradicted` and `schedule_on_bidirectional` (carousel §9) at §7.2 step
|
||
4, and carousel §9's statement and bound codes at §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/`](../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; a claim with
|
||
no group, which stops at step 5; and a node's schedule (§5.6) and a publisher's, a node's chained
|
||
after its claim, and one of a node's refused at each of carousel §4.3's steps it keeps and at
|
||
§4.2 rule 3.3.
|
||
- **Placements**: each step of §4.4, the least origin, a serving role other than `cache/serve`, a
|
||
retired key at its `until` and 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; a
|
||
group that fails; and a node's schedule that stands before and after its claim, is extended, is
|
||
contradicted by its own node, is set aside for each step of §5.6 and credits nothing, a stranger's
|
||
beside it, a publisher's that stands and one on a node's instance, with a statement held to each
|
||
that holds, comes too late, is not within the schedule, has no entry, and is out of bounds.
|
||
- **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 `end` that 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 its `until` among them, a retired key credited on either
|
||
side of its `until`, 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; with a fallback in the group, held by an acknowledgement and a reconciliation; and with
|
||
a ladder of a rung in the group and a rung in another, the first rung and the second each held
|
||
alone, and the first late with the second in time.
|
||
|
||
The population bound of 2<sup>20</sup> 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`), schedules against their group and their extensions and
|
||
availability with the carousel corpus's (`live-conformance/carousel_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, and so is a schedule of
|
||
passes it did not send.
|
||
- **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](transparency.md) §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 2<sup>20</sup> keys; each acknowledgement and reconciliation is read
|
||
once; the byte ranges are sorted once per key; a placement is a lookup per head.
|