open-standards/wai/extensions/revenue-share.md
Transaction Science ba9c439cf9 receipt-ledger §5.2: one registry of record kinds; the job kinds are 14 to 16
Kinds 10 to 13 were already revenue-share §7.4's run and invoice and
opportunity-report §7.3's cue event and room policy; the job source's OER/2
receipt, int_motion decode receipt and route record took 10 to 12 and so meant
two things. They are now 14, 15 and 16. §5.2's table registers every kind a
WAI text names a record by, 10 to 13 pointing to the texts that define them,
unchanged, and those texts say so. Kinds 128 to 255 are reserved for private
and provisional use: a WAI record never states one, and an invoice that lists
one is refused private_kind, in either version. A version-2 invoice lists kinds
1 to 9 and 14 to 16, never another text's. Three controls; the corpus and both
implementations follow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 00:51:06 -04:00

37 KiB
Raw Permalink Blame History

WAI Extension: Revenue Share

Status: Draft. Registers the record wai.delivery.revenue_share: what each content owner of a linear channel is owed from the ads sold in the channel's breaks, as a statement anyone holding the public records recomputes. An operator and every owner it names sign a share contract, WRC1: its period, its channel and the key that signs the channel's program-as-run, the carriers whose ad evidence belongs to the channel, the map from each asset to a split of integer millionths among owners and the operator, and the rule for a break that falls between programmes. The operator signs a statement, WRS1, for one party and one period: the invoices of receipt-ledger it rests on, the program-as-run windows of wai.video.channel it places impressions in, and one line per split and class, each with the impressions attributed, the revenue the invoices' tariffs priced them at, the party's share of it divided by the largest-remainder rule, and the records the line came from. A verifier given the contract, the windows and the ledgers' own verification documents recomputes every line and refuses a statement that does not recompute. No new transport, no payment and no currency conversion: a statement is the recomputable figures a payment to an owner rests on. The reference implementation is the revenue_share module of wai-rs (feature revenue_share); the corpus is live-conformance/share/, and its second implementation is live-conformance/revenue_share_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.

Texts read for this revision: the WAI texts this one cites; ISO 4217 (the alphabetic currency code); RFC 3339 §5.6, for the date-time form of §5.4; RFC 8032; RFC 8259; FIPS 180-4.

Why the name: the extension states how revenue is shared, and the record names both of its parts, the contract and the statement, under one string, as wai.delivery.ledger names its tariff and its invoice.

1. Scope

In scope: the share contract, its splits, its signatures and when two contracts overlap (§3); the program-as-run as a verifier is given it, the JSON carriage of a wai.video.channel receipt, whose signed bytes this text writes out and does not change (§4); the evidence, invoices of the receipt ledger and the impressions their sources state, with their instants (§5); attribution, from an impression's instant to an asset and its split (§6); the statement, its lines, rounding and citations (§7); verification (§8); how a statement composes with the ledger (§9); and the operations (§10).

Out of scope: a transport; paying an owner, an owner's account and any contact with a party outside the records; invoicing a buyer, which the receipt ledger does; converting a currency; how an operator chooses an owner's share; placing in time a presentation whose marker states no time this text reads (§5.4), which is kept in a class of its own; and the records themselves, which their own extensions define, verify and leave as they are. The channel receipt keeps its signed bytes: §4 writes them out for a verifier that is not wai-rs.

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

2. Terms

  • Operator, owner: Ed25519 keys (RFC 8032). The operator carries the channel, sells its breaks and signs contracts and statements; an owner holds programmes the channel airs. A party of a contract is its operator or one of its owners.
  • Time: microseconds since 1970-01-01T00:00:00Z, the unit of receipt-ledger §2. A period is a span of time from its start (included) to its end (excluded). A channel receipt states milliseconds since the same instant; an instant of m milliseconds is m × 1000 microseconds, computed without overflow.
  • Run: one wai.video.channel receipt, a signed program-as-run window (§4). Its window is [window_start_ms, window_end_ms), and an entry is one slot of its schedule.
  • Programme: an entry of kind program. An entry of kind ad or filler is not a programme.
  • Carrier: what delivers the channel's ads: a room of live-ads, named by its room id, whose break-form receipts the ledger bills; or a stitcher of stitched-presentation, named by the key that signs its pod-input records.
  • Ledger document: a verification document of receipt-ledger §7.1, for one invoice.
  • Impression: one ad presented or billed, as the ledger counts it (§5.2).
  • Minor unit, amount: as receipt-ledger §2: an amount is an integer of millionths of a currency's minor unit.
  • ppm: an integer share in millionths, of 10^6.
  • u64, u32, u16, u8: little-endian unsigned integers of that many bits in the records of §3 and §7; the channel receipt's signed bytes (§4.2) are big-endian, as wai.video.channel writes them. A digest is SHA-256 (FIPS 180-4), 32 bytes; zero of a key or digest field is 32 zero bytes. A text field is a u16 length and that many bytes of UTF-8.

3. The share contract

3.1 Layout

field size meaning
magic 4 WRC1 (ASCII)
version 1 1 in this revision
operator 32 the operator's key, which signs the contract and every statement under it
start, end 8 each u64: the period the contract is in force
currency 3 three capitals A–Z (ISO 4217): the currency of every invoice it shares
channel 2 + n a text field of 1 to 1024 bytes: the channel_id of the channel's runs
channel_signer 32 the key that signs the channel's runs
rule 1 for an impression in an entry that is not a programme (§6.2): 1 preceding, 2 following, 3 between
between 2 u16: the index of the between split (§6.2, §6.3)
carriers 2 + 33 × c a u16 count, then each carrier: kind (u8: 1 a room, 2 a stitcher) and id (32: the room id, or the stitcher's key), in strictly ascending order of (kind, id)
owners 1 + 32 × o a u8 count, then each owner's key, in strictly ascending order
splits 2 + … a u16 count, then each split (§3.2)
assets 4 + … a u32 count, then each asset: asset (a text field of 1 to 1024 bytes, an asset_id of the channel's schedule) and split (u16, the index of its split), in strictly ascending order of asset's bytes
operator_sig 64 §3.3
countersigs 64 × o each owner's countersignature, in the order of owners (§3.3)

3.2 Splits

A split divides revenue among parties:

field size meaning
operator_ppm 4 u32: the operator's share, 0 to 10^6
m 1 u8: the owners it names
shares 5 × m each: owner (u8, the owner's index in owners) and ppm (u32, its share), in strictly ascending order of owner

The parties of a split, in their order, are its owners in the order it lists them, then the operator when operator_ppm is above 0. A party holds a share of a split when it is one of its parties. The shares of a split sum to 10^6 exactly: a split is the whole of the revenue it is given, and no part of it is left unassigned.

3.3 Digest and signatures

The body of a contract is its bytes before operator_sig, and its digest is SHA-256 over the body. operator_sig is Ed25519 by operator over the bytes wai:share-contract (ASCII) and 0x01, followed by the body. Each owner's countersignature is Ed25519 by that owner over the bytes wai:share-accept (ASCII) and 0x01, followed by the body; 64 zero bytes in its place state that the owner has not countersigned. An operator offers a contract with every countersignature zero, and each owner fills its own. Every signature is verified strictly, as SPEC §7 Signatures requires: a key that fails step 1 or 2 is bad_key, and a signature that fails a later step is bad_signature.

A contract is in force only when every owner it names has countersigned it: the splits and the asset map bind each owner only by its own signature, and a contract some owner did not sign shares nothing.

3.4 Reading and rules

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

  • start is below end; currency is three capitals A–Z;
  • operator and channel_signer are not zero, and operator is not an owner;
  • channel and every asset are UTF-8 of 1 to 1024 bytes;
  • rule is 1, 2 or 3, and between is the index of a split;
  • carriers has at least one entry, each kind 1 or 2 and each id not zero, in strictly ascending order;
  • owners has at least one key, none zero, in strictly ascending order;
  • splits has at least one split; in each, the owners are in strictly ascending order of index, each index below the count of owners, and each ppm above 0; and every owner holds a share of at least one split;
  • assets has at least one entry, in strictly ascending order, each split the index of a split.

It refuses as split_not_whole a contract with a split whose operator_ppm and ppms do not sum to 10^6. Then it verifies operator_sig (bad_key, bad_signature), then each countersignature in turn: one of 64 zero bytes is not_countersigned, and one that does not verify is bad_key or bad_signature. A refusal of a signature names the operator, or the owner and its index.

3.5 Contracts that overlap

Two contracts overlap when their periods share an instant and they name the same channel, or a carrier in common. An asset aired under two contracts at once would have two owners, and a statement under either would share revenue the other also shares; a verifier holding both refuses a statement under either (§8.2 step 2, contract_overlap). An operator MUST NOT sign two contracts that overlap; one that changes a contract's terms signs a new contract whose period begins where the old one's ends.

4. The program-as-run

4.1 Its JSON carriage

A run is carried as one JSON object (RFC 8259) with exactly these members; a byte field is lowercase hexadecimal of its exact length, and an integer a JSON integer from 0 to 2^64 − 1:

member value
channel_id a string: the channel
schedule_root 32 bytes: §4.2
schedule an array of entries, each {"asset_id", "start_ms", "duration_ms", "kind"}: asset_id a string, kind one of program, ad and filler
window_start_ms, window_end_ms the window
aired an array of integers: the indices of the entries on air in the window
work_schedule_ops the entries' count, as the receipt states it
joules_micro the assembly's energy, or 0 unmetered
energy_provenance its class's label (energy-measurement §2.7: ModelBased, Estimator, OnChipCounter, CalibratedInstrument), or null unmetered
energy_uncertainty, energy_counter_filtering, energy_calibration_ref only for a class that carries them, as live-ads §8.2 writes them
grant {"grant_hash", "capability", "joule_ceiling_micro", "funds_ceiling"}, funds_ceiling null for none
parent_receipt_hash 32 bytes: the run before it in the channel's chain, or null
signer_pubkey 32 bytes: the channel signer's key
signer_id a string
sig 64 bytes: §4.2

A run reads when it is such an object and each entry's start_ms + duration_ms is at most 2^64 − 1. The legacy class label HwShunt (energy-measurement §2.6) does not read in this carriage.

4.2 Its signed bytes and identity

These are the bytes wai.video.channel signs, written out; BLAKE3 throughout, integers big-endian:

  • an entry's leaf: BLAKE3 over wai:video-channel-entry (ASCII) and 0x01, asset_id's length (u32) and bytes, start_ms (u64), duration_ms (u64) and its kind's tag (u8: 1 program, 2 ad, 3 filler);
  • the schedule root: the Merkle root over the leaves in schedule order, jwp-receipts §2.2's tree, each leaf taken as a content hash (its first leaf form) and the final one duplicated on an odd level; all zero for an empty schedule;
  • the signing payload: wai:video-channel-receipt (ASCII) and 0x01; channel_id's length (u32) and bytes; schedule_root; window_start_ms, window_end_ms, work_schedule_ops and joules_micro (u64 each); the class in its signed encoding (energy-measurement §2.7), the tag byte 0 for none; the grant reference as live-ads §8.4 writes it (grant_hash, capability's length (u32) and bytes, joule_ceiling_micro (u64), then 1 and funds_ceiling (u64), or 0); 1 and parent_receipt_hash, or 0; the count of aired (u64) and each index (u64); the count of schedule (u64) and each entry's leaf; and signer_pubkey, then signer_id's length (u32) and bytes;
  • sig: Ed25519 by signer_pubkey over the signing payload, verified strictly;
  • the run's identity, its receipt hash: BLAKE3 over wai:video-channel-receipt-id (ASCII) and 0x01, the signing payload and sig.

4.3 When a run verifies

A run verifies when:

  1. schedule_root is the schedule root of its entries, and work_schedule_ops their count;
  2. window_start_ms is below window_end_ms;
  3. the schedule is continuous: each entry starts where the one before it ends;
  4. aired is exactly the indices, ascending, of the entries that touch the window: those whose start_ms is below window_end_ms and whose start_ms + duration_ms is above window_start_ms;
  5. the figure and its class cohere as live-ads §8.5 rule 5 states it; and
  6. its signature verifies.

So the program-as-run is the schedule the channel signer signed, and what aired in a window is recomputed from it rather than read from a log.

5. The evidence

5.1 Invoices

A statement rests on invoices of the receipt ledger, each given as its ledger document (receipt-ledger §7.1): the tariff, the invoice, the invoice before it in its chain, and the sources. A ledger document is accepted when the receipt ledger's verification (§7.2 there) accepts it. Its tariff states the prices, its invoice the period and the records, and its sources the impressions. A statement prices nothing itself: an impression's revenue is the price the invoice billed it at.

Each invoice of a statement is one the operator sold as the seller of its tariff, in the contract's currency; the buyer is whoever bought the channel's breaks under that tariff. A channel whose breaks several buyers bought rests on several invoices, one chain per tariff.

5.2 Impressions and their classes

An accepted ledger document contributes, from its sources in order:

  • for an ads source whose room is a carrier of kind 1: each receipt in the source's order, and each of its impressions in slot order that the ledger counts (receipt-ledger §6.3 and §8.1):
    • under a policy that requires presentation claims, an impression that counts, its own claim presented (ad-presentation §9.2): class presented, its records the receipt (kind 6) and its own claim (kind 7);
    • under a policy that does not, a billable impression (quartile at least 1): class beacons, its record the receipt (kind 6);
  • for a stitched source: each record the source accepts whose signer is a carrier of kind 2, each of its ads in slot order, and each presentation of the ad (stitched-presentation §7.4, a presented own claim) in the order its claims are given: class presented when its instant is stated (§5.4) and unplaced otherwise, its records the pod-input record (kind 8) and the claim (kind 9).

Record kinds are receipt-ledger §5.2's. Every other source, and every impression of a carrier the contract does not name, contributes nothing: an invoice may bill a buyer for several channels, and a statement shares the impressions of its own.

An impression's price is its invoice's tariff's ads price (receipt-ledger §4.1): per presented impression for the classes presented and unplaced, the unit presented_impressions bills; per billable impression for the class beacons, the unit billable_impressions bills. So an impression is priced at the unit the invoice billed it under, and the classes keep apart what the players' own claims show presented and what rests on a decider's beacons alone. A class is never summed into another: each has its own lines (§7.2).

5.3 Its instant

An impression's instant places it on the channel's timeline:

  • an impression of an ads source: its room's t0 plus the t of the break its receipt names (live-ads §4.1, placed as receipt-ledger §5.3 places it). Every impression of a break shares the break's instant: a break is attributed whole;
  • a presentation of a stitched record: the instant its marker states (§5.4). A presentation whose marker states none has no instant: it is unplaced.

5.4 The instant a marker states

A marker of carriage 1 or 2 (stitched-presentation §3.1) carries the START-DATE of its date range, the second long field of its body. It states an instant when it is exactly this form, in ASCII, of RFC 3339 §5.6's date-time with its letters in capitals:

YYYY "-" MM "-" DD "T" hh ":" mm ":" ss [ "." 1 to 9 digits ] ( "Z" / ( "+" / "-" ) hh ":" mm )

with YYYY a year of four digits, MM 01 to 12, DD 01 to the days of that month in the Gregorian calendar, hh 00 to 23, mm 00 to 59 and ss 00 to 59 (an offset's hh and mm the same), and the instant it names, in microseconds, at or after 1970-01-01T00:00:00Z. The instant is the date and time less the offset, its fraction truncated to the microsecond. A marker of carriage 3 carries no date (§3.4 there): its presentations are unplaced, as are those of a START-DATE that does not state an instant.

6. Attribution

6.1 The run of an instant

The runs a verifier is given are of the contract's channel and signer and do not overlap (§8.2 step 4). The run of an instant is the run whose window holds it; the entry of the instant is the entry of that run's schedule whose span holds it. An impression with an instant and no run, or a run and no entry (an instant past its schedule's end), aired in no window the verifier holds: it is refused outside_window.

6.2 The asset and its split

The entry of an impression's instant gives its asset:

  • a programme gives its own asset_id: a break inside a programme is that programme's;
  • an entry that is not a programme gives, by the contract's rule: for preceding, the asset_id of the last programme before it in the run's schedule; for following, that of the first programme after it; when there is no such programme, or for between, no asset.

An impression with an asset is attributed to the asset's split in the contract's assets; an asset the contract does not map is refused asset_unmapped. One with no asset is attributed to the between split. The rule applies to the run's own signed schedule, so anyone holding the run finds the same programme.

6.3 Unplaced impressions

An unplaced impression is attributed to the between split, in the class unplaced. It is revenue the channel earned in some break that no record places in a programme, and the contract's between split is how its parties agreed to share such revenue. It is never counted in the class presented: a statement states it apart, and an owner sees how much of its revenue no record placed.

7. The statement

7.1 Layout

field size meaning
magic 4 WRS1 (ASCII)
version 1 1 in this revision
contract 32 the contract's digest (§3.3)
party 32 the party it is for: the operator or an owner
start, end 8 each u64: the period it states
currency 3 the contract's
invoices 2 + 32 × i a u16 count, then each invoice's digest (receipt-ledger §6.7), in strictly ascending order
runs 2 + 32 × r a u16 count, then each run's identity (§4.2), in strictly ascending order
lines 2 + 67 × l a u16 count, then each line (§7.2), in strictly ascending order of (split, class)
total 8 u64: the sum of the lines' amounts
sig 64 §7.5

7.2 Lines

A line is 67 bytes:

field size meaning
split 2 u16: the split's index
class 1 1 presented, 2 beacons, 3 unplaced (§5.2)
impressions 8 u64: the impressions of the class attributed to the split
revenue 8 u64: the sum of their prices
ppm 4 u32: the party's share of the split
amount 8 u64: the party's part of the revenue (§7.3)
cited 4 u32: the records the line cites (§7.4)
cites 32 their citation digest (§7.4)

There is one line for each split of which the party holds a share and each class with at least one impression attributed to that split, and none other. A party's statement so states the whole revenue of every split it shares in, and its part of each.

7.3 Revenue, shares and rounding

A line's revenue is divided among the parties of its split by the largest-remainder rule, so that the parts are integers and sum to the revenue exactly:

  1. For each party i of the split, in its order (§3.2), its quota is revenue × ppmᵢ, computed in integers wide enough not to overflow; its base is ⌊quota / 10^6⌋, and its remainder is quota − base × 10^6.
  2. The leftover is revenue less the sum of the bases. It is less than the count of parties.
  3. The parties with the largest remainders, as many as the leftover, each receive one more than their base; between equal remainders, the earlier party in the order receives it first.
  4. A party's part is its base, plus one when step 3 gives it one.

The parts of a split's parties sum to its revenue, whoever's statement states them: the operator's statement and each owner's state the same revenue for a split and class, and their parts sum to it. Rounding is applied once per split and class over the statement's period, never per impression, so it does not accumulate.

7.4 Citations

Each line cites the records it came from: for each impression attributed to its split in its class, the impression's records (§5.2), the run that placed it (kind 10, its identity) when it has an instant, and the invoice that billed it (kind 11, its digest). cited is their number, each record counted once, and cites is SHA-256 over the bytes wai:share-cites (ASCII) and 0x01, followed by each cited record's kind (u8) and identity (32), in ascending order of (kind, identity). A line that cites no record has no source (line_unsourced, §8.2 step 7). An owner shows one line to an auditor with the records it cites, the contract and the runs. Kinds 10 and 11, like every kind a citation digest names, are registered in receipt-ledger §5.2.

7.5 Digest and signature

The digest of a statement is SHA-256 over its bytes before sig. Its signature is Ed25519 by the contract's operator over the bytes wai:share-statement (ASCII) and 0x01, followed by the bytes the digest covers, verified strictly (SPEC §7 Signatures).

7.6 Reading and rules

A reader reads exactly the bytes it is given as one statement, as §3.4 reads a contract with WRS1 for WRC1 (truncated, malformed, unknown_version), and refuses as share_statement_invalid one that breaks any of these:

  • start is below end; currency is three capitals A–Z; party is not zero;
  • invoices, runs and lines are each in the strictly ascending order §7.1 gives it;
  • each line's class is 1, 2 or 3, its impressions and cited above 0, its ppm 1 to 10^6, and its amount at most its revenue.

Its signature is checked against the contract's operator (§8.2 step 3).

8. Verification

8.1 Inputs

A verifier is given the contract; the other contracts it holds of the operator or of the channel, for the overlap check; the statement; the runs; and the ledger documents. Its verification document holds them as one JSON object with exactly the members contract and statement (hexadecimal), contracts (a list of hexadecimal strings), runs (a list of runs in their carriage, §4.1) and ledgers (a list of ledger documents, receipt-ledger §7.1). A document that is not such an object is refused malformed.

8.2 Steps

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

  1. The contract (§3.4), at contract; a signature's refusal at contract and operator, or contract, owner and the owner's index: truncated, malformed, unknown_version, contract_invalid, split_not_whole, bad_key, bad_signature, not_countersigned.
  2. The other contracts, each at contracts and its index, a signature's place after it: each is read and verified as step 1, with those reasons; one whose digest is the contract's is the contract and is passed over; one that overlaps the contract (§3.5) is contract_overlap.
  3. The statement, at statement: it reads (§7.6: truncated, malformed, unknown_version, share_statement_invalid); its contract is the contract's digest, its party the operator or an owner of the contract, and its currency the contract's: otherwise contract_mismatch; its period lies within the contract's: otherwise outside_contract; its signature verifies under the operator (§7.5): bad_key, bad_signature.
  4. The runs, each at run and its index: it reads (§4.1: malformed); it verifies (§4.3: run_invalid); its channel_id is the contract's channel and its signer_pubkey the channel_signer: otherwise run_foreign; its window shares an instant with the window of a run before it: run_overlap.
  5. The ledger documents, each at ledger and its index: it is verified as receipt-ledger §7.2 verifies one, a refusal being that extension's reason with its place after the index; its invoice's seller is the operator and its currency the contract's: otherwise invoice_foreign; its period lies within the statement's: otherwise outside_period; and no record of kinds 6 to 9 in its invoice's records is in the records of an invoice before it, and no ad receipt of its sources names the room, the cue and the session an ad receipt of a document before it names: otherwise impression_duplicate. A ledger refuses a record billed twice within one invoice and one chain (receipt-ledger §5.2, §6.9); this step refuses one billed by two invoices of a statement, under two tariffs among them.
  6. The impressions (§5.2), in the order the documents give them, each at ledger, its index, source and the source's index: an impression with an instant (§5.3) whose instant does not lie within the statement's period is outside_period; one that aired in no window (§6.1) is outside_window; one whose asset the contract does not map (§6.2) is asset_unmapped.
  7. The figures, at statement, recomputed from the impressions as §7.2 to §7.4 compute them for the statement's party, and compared in this order:
    1. a line whose split and class give the party no impression, or that states more impressions than they give: line_unsourced, a line no signed record covers;
    2. a line whose cited or cites is not the records it came from: citation_mismatch;
    3. any other difference (the invoices, the runs, a line missing, fewer impressions, a revenue, a ppm, an amount, the total): share_statement_mismatch.

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

8.3 Outcome

  • refuse: the reason and where.
  • accept: the statement's digest; the contract's digest; the party and whether it is the operator or an owner; the currency and the period; the invoices and the runs; each impression of the contract's carriers, in the order of step 6, with the ledger document and source it came from, its records, its class and price, its instant, run, entry and asset (each null when it has none) and its split; each line with its split, class name, impressions, revenue, the party's ppm and amount, its cited records, and the division, every party of its split with its ppm and part; and the total.

The division is not signed: anyone holding the records computes it, and it shows that the parts of a split sum to its revenue.

9. The statement and the receipt ledger

A statement composes with the ledger and duplicates none of it:

  • Its sources are the ledger's. The impressions a statement shares are those of the ads and stitched sources of invoices the ledger accepted, verified as the ledger verifies them: by live-ads with ad-presentation, and by stitched-presentation. A statement recomputes no decision, no claim and no presentation; it reads the ledger's counts and verdicts.
  • Its prices are the tariff's. An impression's revenue is its invoice's price for the unit that billed it, so an owner's revenue is a part of what the buyer was invoiced, never a figure the operator states.
  • Its records are the invoice's. Each impression is named by the records the invoice holds, and the statement cites the invoice itself. A statement adds the two things the ledger does not hold: the instant of each impression on the channel's program-as-run, and the split the contract gives the programme that aired at that instant.
  • An operator that bills a channel's breaks puts the sources of the channel's carriers into the ledger as the ledger receives any source (receipt-ledger §5.1, §9), under a tariff whose scope names the carriers' deciders and stitchers (receipt-ledger §4.4). The verification document the ledger keeps with each invoice (receipt-ledger §9: GET /v1/invoices/{digest}) is the ledger document a statement is given, unchanged.

Paying an owner its part, and an owner invoicing the operator for it, are not sources the ledger defines, and this revision defines none.

10. Operations

wai runs the operations of this extension, under the agent rules of the operation catalogue (the --json envelope, the exit codes, --dry-run on every operation that writes, --yes before a file is replaced). A refusal by this text has its reason as the operation's code.

operation command line does
contract.create wai contract create <terms.json> --key <file> [--out F] the operator's contract from its terms, signed by the operator with every countersignature zero (§3.3)
contract.update wai contract accept <contract> --key <file> [--out F] an owner's countersignature added, after the contract's rules and the operator's signature are checked
statement.create wai statement create <inputs.json> --party <key> --start µs --end µs --key <file> [--out F] the statement of a party and a period, computed from a verification document without its statement (§8.1), signed by the operator, written with its document
statement.verify wai statement verify <document.json> a verification document's outcome (§8.3); exit 1 with the reason on a refusal

11. Reasons

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

reason raised at
contract_invalid §3.4, §8.2 steps 1 and 2
split_not_whole §3.2, §3.4, §8.2 steps 1 and 2
not_countersigned §3.3, §8.2 steps 1 and 2
contract_overlap §3.5, §8.2 step 2
share_statement_invalid §7.6, §8.2 step 3
contract_mismatch §8.2 step 3
outside_contract §8.2 step 3
run_invalid §4.3, §8.2 step 4
run_foreign §8.2 step 4
run_overlap §6.1, §8.2 step 4
invoice_foreign §5.1, §8.2 step 5
impression_duplicate §8.2 step 5
outside_window §6.1, §8.2 step 6
asset_unmapped §6.2, §8.2 step 6
share_statement_mismatch §8.2 step 7

A verifier of this extension also raises, with the meaning this text gives them: truncated, malformed and unknown_version for a contract or a statement that does not read, and malformed for a document or a run that does not read; bad_key and bad_signature for their signatures; outside_period (receipt-ledger §10) for an invoice or an impression outside the statement's period; line_unsourced (fleet §10) for a line no impression gives; citation_mismatch (receipt-ledger §10) for a line's citations; and, at a ledger document, every code the receipt ledger raises (receipt-ledger §10 and the codes it lists).

12. Conformance

live-conformance/share/ holds the corpus, in one file, vectors.json, written by wai_revenue_share_vectors gen from fixed seeds and from the sources of the ledger corpus (live-conformance/ledger/): a room's break-form receipts under a policy that requires claims and under one that does not, and three streams stitched by others, one of whose markers are DASH events. The ledger documents, the runs and the contracts are held once, by name, and each vector is a verification document (§8.1) naming them, with the outcome §8.3 gives it:

  • Accepted: a channel's month, its two windows airing two owners' programmes with mid-roll breaks and an ad slot between programmes, its impressions from two invoices of one chain, one of the room's breaks and one of the stitched streams; the statement of each owner and of the operator under the rule preceding, so that the three statements' parts of each split sum to its revenue; and an owner's statement under the rules following and between.
  • Refused: each reason of §11 at least once, each vector a control of one defect; each code §11 reuses; and a ledger document refused by the receipt ledger, at its place.

wai_revenue_share_vectors verify and the test tests/revenue_share.rs check that the file regenerates byte for byte, that wai-rs gives every vector its outcome, that every reason of §11 refuses a vector, and that §11 lists exactly the reference's codes; the test also holds the worked example, three statements of one month computed and then recomputed from the public records alone, and the module's unit tests the rounding rule's ties. live-conformance/revenue_share_verify.py is the second implementation, written from this text: it shares no code with wai-rs, verifies signatures with WAI's strict rule written from RFC 8032 (claim-conformance/_ed25519.py) and BLAKE3 with claim-conformance/_blake3.py, verifies each ledger document with that corpus's own second implementation (receipt_ledger_verify.py), reads every contract, run, marker and statement itself, recomputes every attribution, line, division and citation, gives every vector the same outcome, requires every mutated copy of the corpus 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

  • Who signs what. Each owner signs the splits and the asset map it is bound by; the channel signer signs what aired; the decider, the stitcher and each player sign the impressions; the buyer signs the prices; the operator signs the statement. An operator cannot move an impression to another programme, price it at another price or round a part against an owner: the verifier recomputes every line from records the operator did not sign alone.
  • What the operator still controls. The records show what was invoiced from the contract's carriers. An operator that sells a break without invoicing it, or carries the channel through a room or a stitcher the contract does not name, keeps that revenue out of every statement; the contract's carriers are what the owners agreed to, and an owner that suspects more carriers asks for them by name.
  • The between split. Revenue in an entry that is not a programme, and every unplaced presentation, goes to the between split. An owner that agrees a between split in which it holds no share agrees to receive nothing from such breaks, and a statement states their revenue apart so that the owner sees it.
  • Double counting. One invoice and one chain bill a record once (the ledger's rules); two invoices of one statement may not bill one impression (impression_duplicate); two contracts may not share one channel or carrier at once (contract_overlap); and two runs may not place one instant (run_overlap). A verifier holding fewer contracts than the operator signed cannot see an overlap with the ones it lacks.
  • What a statement reveals. A statement names the invoices and runs it rests on, and a line states its split's whole revenue: the co-owners of a split, and the operator, see the same figures. A verification document holds the ledgers' sources, whose receipts name sessions and whose claims name session keys made for one session alone (ad-presentation §3, stitched-presentation §5.2). It travels between the operator, the owner and an auditor they choose.