WAI Extension: Impression Aggregates
Mirrored from the canonical text at commit 260cbd09 ().
Status: Draft. Registers the record
wai.delivery.impressionsand measure 5,impressions, of telemetry-aggregation §3 (its §3.1): ad presentations counted over a bearer with no return path. A receiver judges each ad of a shared pod-input record it presented by stitched-presentation’s rules, and turns each presentation into one Prio3Histogram report of a task of that measure, whosescopeis the digest of a catalogue (WIC1, §4) that fixes which index of the vector is which creative, region and quartile. It generates the report once, keeps its bytes, and sends them over whatever path it later has, in an upload file (WIU1, §6.4) that relays forward unchanged. It accepts a task only from a task file (WIT1, §5.4) whose signer its pinned key log lists with the authorityaggregate/task, and with attestation each report carries a Privacy Pass token bound to its task and its report id, in a private report extension (§7.2). The leader holds each collection for the catalogue’shold_s, and the aggregators keep the task until its retention instant, so late reports still count (§7.1). The task layout, the task id, the aggregators, the statements (WQS1) and the receipt (WQA1) are telemetry-aggregation’s; a reader verifies a receipt against its catalogue and recomputes a table of presentations and quartile counts per creative and region (§8): a lower bound of what receivers reported, never an audience, beside what each aggregator states it rejected (§7.3). The reference implementation is theimpression_aggregatemodule ofwai-rs(featureimpression_aggregate); the corpus islive-conformance/impressions/, and its second implementation islive-conformance/impression_aggregate_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.Texts read for this revision:
draft-ietf-ppm-dap-20(DAP) §2.3, §3.1, §3.6, §4.1, §4.1.1, §4.2, §4.2.2, §4.2.3, §4.4.1, §4.4.2, §4.4.2.1, §4.4.2.2, §4.4.3, §4.5.3.3, §4.5.3.4, §4.5.4.3, §4.6.1, §5.1, §6.1.1, §6.3, §6.4.1, §8.1 to §8.4, §8.6.4, §9.2, §9.2.4 and Appendix B.4;draft-irtf-cfrg-vdaf-22(VDAF) §7.4.3.1, §7.4.4, §7.4.5, §9.2, Table 4, Table 13 and §10 Table 19; RFC 9576 §3.3; RFC 9577 §2.1.1, §2.1.1.2 and §2.2.1; RFC 9578 §6.4 and §6.5; and, informatively,draft-ietf-privacypass-batched-tokens-10§2, §3 and §7. ISO 3166-1 and ISO 3166-2 were not read; the code form is the one rung-price §2 states. A clause number of one of them in this text is that text’s.
1. Scope
In scope: the catalogue (§4); measure 5’s task, its rules and the task file (§5); the receiver’s contribution, report, upload file, upload plan and settlement (§6); what the aggregators add to DAP: retention, the hold, the token check and what each aggregator rejected (§7); the verification of a receipt of an impression task, and the impression table (§8).
Out of scope: issuing tokens (RFC 9578, and the batched issuance of
draft-ietf-privacypass-batched-tokens-10); how a receiver learns its region or sets its clock;
running aggregators; choosing a helper’s operator (DAP §8.7); differential privacy; prices and
invoices; an audience or a reach; and two-way paths, which keep
stream-audit and stitched-presentation.
Relationship to the core spec: wai.delivery.impressions is a record (SPEC
§5). It names no payload format and is not dispatchable.
Relationship to telemetry-aggregation: a task of measure 5 is one of its tasks. This text supplies the measure’s configuration and rules (§5.3), its reports (§6.3), what its aggregators add (§7) and the layer a reader puts over its accepted receipts (§8). Every other rule of that text stands.
2. Terms
- DAP, task, report, leader, helper, collector, batch, batch interval, measure: as telemetry-aggregation §2.
- Record, claim, marker, presented: as stitched-presentation §2.
- Creative digest: as ad-presentation §4. Token: as ad-presentation §2.
- Key log, anchor, pinned, confirmed: as jwp-receipts §7.
- Region code, holds: as rung-price §2.
- Instant: a count of milliseconds since 1970-01-01T00:00:00Z, leap seconds not counted, as carousel §2 and holdings §2.
- Receiver: the sink that presents the ads of a bearer and reports them.
- Presentation: a claim the receiver’s player signs that is presented under §6.2.
- Flight: a task’s
task_interval(DAP §4.2.3). - Retention instant: §5.3 step 3.
- Relay: any node that carries an upload file toward the leader.
3. What one-way delivery changes
Telemetry-aggregation §5 (its §5.1) has a sink report when its session ends, over a path it has then, and DAP §6.1.1 says that “Clients are not expected to store any state across any upload flows, nor are they required to implement any sort of report upload retry mechanism”. On a bearer with no return path:
- Late reports. A receiver reports hours or days after it presented. DAP discards a report
of a batch already collected (§4.4.2.2), and aggregators may delete a task after its
task_interval(§6.4.1). So the catalogue states a hold; the leader collects no batch before its end plus the hold, and the aggregators keep the task until the retention instant (§7.1); a receiver sends the reports nearest their deadline first (§6.5). A report later than its batch’s collection is lost: a count is lower, never higher. - Duplicates. A receiver that crashed, kept two copies of its state, or sent by two relays could count one presentation twice. So a report is generated once, stored, and re-sent as the same bytes until it is settled (§6.3, §6.6), and DAP’s report-id check discards every later copy. A playback session gives one report for a record and an ad (stitched-presentation §7.3 step 7). A report is never regenerated: its id is VDAF §9.2’s nonce, and a second id would be a second count.
- Replay. A captured upload replayed to the same task is discarded by its ids. Replayed to another task, its shares do not decrypt, since their authenticated data holds the task. A token answers one report id of one task (§7.2) and travels sealed.
- A receiver that never returns. Its presentations are never counted. A table is a lower bound on reported presentations, never an audience, and never the n of bearer-receipts §7 (§8.3).
- No path at play time. No challenge reaches a receiver before an ad plays, so a token cannot be bound to the presentation as ad-presentation §7.2 binds one; it is bound to a report id drawn ahead (§6.1, §7.2). No HPKE configuration can be fetched (DAP §4.4.1), so the task file carries them (§5.4). No task is trusted because it arrived on the bearer, so its signer must be confirmed under a pinned anchor (§5.4; unidirectional-delivery §3).
4. The catalogue
4.1 Layout
A catalogue, WIC1, fixes the meaning of a task’s vector. Integers are big-endian, except
inside terms, which is copied byte for byte from a pod-input record and is little-endian there
(stitched-presentation §4). A short field is a u8 length and that many bytes.
| size | field | meaning |
|---|---|---|
| 4 | magic | WIC1 (ASCII) |
| 1 | version | 1 |
| 4 + long | terms | the measurement terms exactly as a pod-input record carries them (stitched-presentation §4.1): presentation (1), viewability (1), token_type (u16), issuer (a u16 length, then its bytes); ad-presentation §8’s fields |
| 4 | hold_s | u32: the seconds after a batch interval ends before the leader completes a collection of it (§7.1) |
| 1 | s | the signers’ count |
32 × s | signers | Ed25519 keys whose shared pod-input records count, strictly ascending |
| 1 | c | the creatives’ count |
32 × c | creatives | creative digests (ad-presentation §4), strictly ascending |
| 1 | r | the named regions’ count |
short × r | regions | region codes, strictly ascending by bytes |
| 1 | k | the issuer keys’ count |
(2 + n) × k | keys | each a u16 length, then the DER SubjectPublicKeyInfo of a type-0x0002 issuer key (ad-presentation §7.3), strictly ascending by bytes |
The catalogue’s digest is SHA-256 over all its bytes. It carries no signature: a task binds
it as its scope (§5.1), which enters every input share, every aggregate share and the
collector-signed receipt, and a task file signs it (§5.4).
4.2 Regions and cells
- A code is a region code of rung-price §2: an ISO 3166-1 alpha-2 code, or an ISO 3166-2 code, in capitals.
- Column j, for 0 ≤ j <
r, isregions[j]; columnrisother. - A receiver’s region g, a byte string or none, has the column of the listed code that holds
g (rung-price §2 Holds: the code itself, or the country of which g is a subdivision),
and otherwise
other. Rule 6 of §4.3 leaves at most one such code. A g that is none, or is not a code, has columnother. Relations among ISO 3166-2 subdivisions are not followed: no code shows its parent, so a receiver in a subdivision of a listed subdivision isotherunless its declared code is the listed one. A deployment lists codes at the granularity its receivers declare.
With creative index i (the catalogue’s order), column j and quartile q (1 to 5,
stitched-presentation §7.4), the cell is (i · (r + 1) + j) · 5 + (q − 1), and the
vector’s length is L = c · (r + 1) · 5. The chunk length is ⌈√L⌉, the least integer
whose square is at least L: VDAF §7.4.4 recommends a chunk length “near the square root of
length”, and §7.4.3.1 says the square root gives proofs “up to 50% larger than the optimal”. It
fixes the configuration as one value.
4.3 Reading and rules
A reader reads exactly the given bytes, at most 1,048,576 of them. It refuses them
catalogue_malformed when they end before a field, go on past the last key, or when:
magicis notWIC1, orversionis not 1;termsbreaks ad-presentation §8, does not set bit 0 (claim), or sets attest with atoken_typeother than 2;hold_sis 0: a collection at a bucket’s end loses every report of a receiver that had no path at that moment;sis 0 or above 16, orsignersis not strictly ascending;cis 0, orcreativesis not strictly ascending;- a region is not a code;
regionsis not strictly ascending; or a listed two-byte code x has a listed code beginning x-(rung-price §4.5 rule 3’s rule, for one footprint); - L is above 4,096. L is a multiple of 5, so the largest is 4,095, and a leader’s measurement share is then at most 65,520 bytes (Field128: VDAF Table 4 and Table 13);
kis not 0 when attest is clear, or not 1 to 4 when it is set; a key is not an id-RSASSA-PSS SubjectPublicKeyInfo with a 2,048-bit modulus (ad-presentation §7.3); orkeysis not strictly ascending by bytes.
Each catalogue has exactly one encoding. The size bound decides no outcome: a catalogue that meets these rules is under 340,000 bytes, its only long fields being the issuer and at most four keys, each of a u16 length, so a longer buffer is refused whatever the bound. It lets a reader refuse one without reading it.
5. The task
5.1 Measure 5 and its scope
A task of telemetry-aggregation’s measure 5, impressions, has that text’s task_info
(telemetry-aggregation §4.1: 153 bytes, label wai-qoe1) with measure 5 and scope the
digest of its catalogue. Its VDAF is Prio3Histogram (VDAF §7.4.4, identifier 0x00000004 in
§10 Table 19) of the catalogue’s L and chunk length, two aggregators. Its measurement is one
presentation’s cell (§6.2).
5.2 Task id and verification key
Its task id is telemetry-aggregation §4.2’s, and its verification key §4.4’s. Two catalogues give two scopes, two TaskConfigurations and two ids.
5.3 Rules
Given a catalogue C that reads (§4.3), a TaskConfiguration is an impression task of C unless, in order:
task_invalid: telemetry-aggregation §4.3 rule 1 refuses it;catalogue_mismatch: itsmeasureis not 5, or itsscopeis not C’s digest;task_invalidwhen itstime_precisionis below 3,600; its extensions are not exactly onetask_interval(DAP §4.2.3) holding an Interval of a duration of at least 1; or its retention instant, in seconds, (task_interval.start+task_interval.duration) ·time_precision+hold_s, overflows a u64 or is above the last instant in seconds, 253,402,300,799 (carousel §4.2; timeliness §2);measure_mismatch:vdaf_typeis not 0x00000004, orvdaf_configurationis not Prio3HistogramConfig {lengthL,chunk_length⌈√L⌉} (DAP Appendix B.4);floor_too_low:min_batch_sizeis below 50;aggregator_alone:leaderishelper.
These are telemetry-aggregation §4.3 for a task of measure 5: its rule 2 cannot fail, since 5 is a code of its §3, and its rule 3 is steps 2 to 4 here. The codes of steps 1 and 3 to 6 are that text’s, with its meaning.
5.4 The task file
A task file, WIT1, is how a task reaches receivers. Integers are big-endian.
| size | field | meaning |
|---|---|---|
| 4 | magic | WIT1 (ASCII) |
| 1 | version | 1 |
| 2 + n | task | a u16 length, then the TaskConfiguration (DAP §4.2) |
| 4 + n | catalogue | a u32 length, then the catalogue |
| n | leader_hpke | the leader’s HpkeConfigList as DAP §4.4.1 encodes it, its own u16 length first |
| n | helper_hpke | the helper’s |
| 32 | signer | an Ed25519 key |
| 2 + n | signer_id | a u16 length, then the signer’s identity string (jwp-receipts §7.1), ASCII |
| 64 | sig | Ed25519 by signer over wai:impression-task (ASCII), 0x01, then every byte before sig, verified strictly (SPEC §7 Signatures) |
The task file’s digest is SHA-256 over the bytes before sig. Its signed bytes include
signer and signer_id, so it is a receipt of another WAI family that jwp-receipts §7.4
confirms.
A receiver holds key-log revisions and anchor records as bearer-receipts §8 (its §8.3) says: provisioned logs pinned, and logs that arrive on the bearer held. It accepts a task file unless, in order:
task_file_malformed: the file exceeds 1,048,576 bytes, has another magic or version, ends early or has bytes aftersig, or itssigner_idis empty or not ASCII; or an HpkeConfigList does not read (DAP §4.4.1:HpkeConfig HpkeConfigList<10..2^16-1>), holds two configurations of oneid(DAP §4.4.1: “Aggregators MUST allocate distinct id values”), or holds none of KEM 0x0020, KDF 0x0001 and AEAD 0x0001 with a 32-byte key (DAP §7; telemetry-aggregation §5);catalogue_malformed: the catalogue does not read (§4.3);- the task fails §5.3 against that catalogue, with that step’s reason;
bad_key,bad_signature: the signature does not verify (SPEC §7);- the signer is not confirmed under jwp-receipts §7.4 with the authority
aggregate/task, which is pinned-only (jwp-receipts §7.5), the item untimed: the reason of the step that fails, one of jwp-receipts §7.7’s (signer_id_not_origin_bound,signer_id_names_another_key,origin_not_anchored, a key-log reason of step 3,key_not_listed,key_mismatch,key_revoked,authority_not_granted,anchor_not_pinned,origin_not_expected). Unconfirmed and refused are both not accepted; the reason says which.
A file that reaches step 5 has been read, checked and signature-verified; only the trust
decision remains. A revocation of the task key in a revision that arrived on the bearer reaches
a receiver whose anchor that revision descends from (bearer-receipts §8.2), and refuses the
file key_revoked. An untimed item passes a retired key (jwp-receipts §7.4 step 5): an origin
stops a task key by revoking it.
6. The receiver
6.1 What a receiver holds
- The task files it accepted, and the key logs it confirmed them under.
- Its region and its clock, as its own declared state (unidirectional-delivery §2).
- For a catalogue that sets attest, a pool of pairs (report id, token) for the task. Each id comes from a cryptographically secure random number generator (VDAF §9.2; DAP §4.4.2). Each token is a type-0x0002 token for §7.2’s TokenChallenge of that id, obtained while the receiver had a path, by RFC 9578 issuance or by batched issuance. A receiver SHOULD refill its pool to a fixed size rather than by what it used since the last refill, so issuance shows the issuer a rate and not a count of presentations.
Evaluating anything here MUST NOT cause a request (unidirectional-delivery §2). A receiver sends only §6.4’s upload files, and token requests, on a path it already has.
6.2 Contribution
Inputs: an accepted task file; the pod-input record, by its bytes; the manifests the player
read; the creatives it holds; claims, the claims of this playback session already judged and
then the one judged now, each signed by the session key (stitched-presentation §5.2); region,
bytes or none; at_ms, the instant the presentation ended by the receiver’s clock, or none when
the clock is not set; and pool, the unused pairs of an attested catalogue.
Output: report, with the cell, the creative index, the column, the quartile, the time
⌊at_ms / (1000 · time_precision)⌋, the report id (the chosen pair’s, or a fresh one without
attestation) and the pool’s ids discarded; or no report, with the reason of the first step
that fails, and the ids discarded:
- The record, under stitched-presentation §7.2 steps 1 to 3 with the catalogue’s
signersas the keys it accepts. Its response is not checked, as that section’s step 5 allows, and no price is used. Reasons:pod_input_malformed,bad_key,bad_signature,pod_signer_unknown. record_not_shared: the record’ssessionis not 16 zero bytes. A record of one session needs the session key to reach its signer (stitched-presentation §4.6), which a receiver with no path cannot do.record_terms_differ: the record’s measurement-term bytes are not the catalogue’sterms. Every count of a table means one rule.- The claim judged now, under stitched-presentation §7.3 steps 1 to 10, over
claimsin order, against that record: its reason (stitched_claim_malformed,bad_key,bad_signature,claim_key_unbound,claim_no_decision,claim_manifest_unknown,claim_marker_absent,claim_duplicate,claim_asset_mismatch,claim_frame_mismatch,claim_not_viewable). Step 11 is not run: with attest, the report’s token takes its place (§7.2).claim_duplicatehere is one report for a record, an ad and a session. creative_not_listed: the ad’s creative digest is not amongcreatives.instant_unknown:at_msis none. The upload time is never used in its place.outside_task: the time is not withintask_interval, which is half-open (DAP §4.1.1).token_exhausted: the catalogue sets attest and no pair ofpoolpasses §7.2 for this task. Pairs are tried in ascending order of report id; a pair whose token fails is discarded and listed.
Otherwise the quartile q is stitched-presentation §7.4’s for the claim, the column is §4.2’s
for region, and the cell is §4.2’s.
6.3 The report
For a contribution that gives a report, the receiver generates one report as soon as the presentation ends:
- Report id: the chosen pair’s with attest; otherwise 16 bytes from a cryptographically secure random number generator.
- Time: the contribution’s.
- Measurement: the one-hot vector of length L at the cell.
- Sharding and sealing: as DAP §4.4.2.1 and telemetry-aggregation §5 (its §5.1), with the
application context
dap-20followed by the task id; each input share sealed to the first configuration of its aggregator’s list in the task file with KEM 0x0020, KDF 0x0001 and AEAD 0x0001, since DAP §4.4.1 lists configurations “in decreasing order of preference”. - Extensions: no public extension. With attest, the private extension
wai_impression_token(§7.2) in both input shares, its data the pair’s token in ad-presentation §7.1’s form (354 bytes). Without it, no private extension. - Persistence: the receiver stores the report’s bytes under the presentation’s key (the record’s digest, the ad’s slot and the session), atomically, before anything is sent. It MUST NOT generate a second report for a presentation that has a stored one, and MUST NOT regenerate a report. VDAF §9.2: the nonce must come from a cryptographically secure random number generator, and “Uniqueness of the nonce is not sufficient”; a regenerated report has a new id while an earlier copy may still reach the leader by another path.
This departs from DAP §6.1.1 in what it expects of a client, not in anything DAP forbids. DAP §4.4.2.2’s “When the Client receives an hpke_unknown_config_id error, it SHOULD invalidate any cached HpkeConfigList and retry with a freshly generated Report” is not followed: that is the regeneration this section forbids (§6.6).
6.4 The upload file
| size | field | meaning |
|---|---|---|
| 4 | magic | WIU1 (ASCII) |
| 1 | version | 1 |
| 32 | task_id | the task the reports were made for |
| 4 + n | upload | a u32 length, then a DAP UploadRequest: one or more Reports (DAP §4.4.2), in strictly ascending order of report id |
A reader reads exactly the given bytes, at most 16,777,216 of them, and refuses them
upload_malformed when they end early or go on past upload, the magic or the version is
wrong, upload holds no report, a report does not read as a DAP Report, or the ids are not
strictly ascending.
- The file is unsigned: DAP reports are anonymous, and each input share’s authenticated data binds its task.
- It travels as bearer-receipts §9 (its §9.1) sends a reconciliation: when the receiver has a path; and where a bearer’s own reporting carries files or opaque payloads, it MAY travel there, for a gateway to forward.
- A relay forwards
uploadunchanged as the body of a POST to{leader}/tasks/{task-id}/reports(DAP §4.4.2), the leader being theleader_aggregator_endpointof the TaskConfiguration of the task file oftask_id, which the relay holds: the task file rode the bearer. It MAY do so over Oblivious HTTP (RFC 9458; DAP §8.4). - A relay MAY merge files of one
task_idinto one, ascending by id. Two reports of one id with equal bytes are one; with different bytes, each is forwarded in a separate file and the leader keeps the first. A relay that merged gives each source file the entries of the response whose ids are its own, in their order. Merging hides from the leader which reports came from one receiver.
6.5 The upload plan
Inputs: an accepted task file, the stored reports (bytes), an instant t (ms) and a budget b (bytes, or none for no limit).
For a report of time τ, in this order: it is dropped when τ is outside task_interval, or
when t is at or past the retention instant · 1000, since the aggregators may then have deleted
the task (DAP §6.4.1). Otherwise its deadline is ((τ + 1) · time_precision + hold_s) ·
1000, which cannot overflow since τ is within the flight: the earliest instant the hold allows
any batch holding τ to be collected. It is timely when t is before its deadline, and
late otherwise; a late report may still count.
Order: timely before late; within each, ascending deadline, then ascending id. Budget: reports are taken in that order while 41 plus the sum of their lengths stays within b; the first that does not fit ends the taking. One task’s reports are all of one length (one L, one set of extensions), so nothing smaller follows.
Outputs: send (ids in order, each with its class), defer (ids), drop (ids), and file:
the WIU1 of send in ascending order of id, or none when send is empty.
6.6 Settlement
Inputs: a WIU1 the receiver sent, and the response body.
- The response reads as DAP’s UploadErrors (DAP §4.4.2.2: ReportUploadStatus entries of a 16-byte id and a 1-byte error, to the message’s end, “in the same order as in the request”) whose ids are a subsequence of the file’s. Otherwise it settles nothing, and every report is kept.
- A report with no entry was accepted: delete it.
report_too_early(8): keep it; DAP §4.4.2.2: “the Client MAY re-upload the report later on”.- Any other report error of DAP §4.1 (1 to 7, 9 and 10): delete it. That includes
report_replayed(2), which a leader MAY set for a report of a batch already collected (DAP §4.4.2.2), andhpke_unknown_config_id(4), whose retry would be a regeneration: the report is lost, an undercount. - A value DAP §4.1 does not define: keep it. A re-send never counts twice (the report id), so keeping costs bandwidth only, and an unknown value never reads as final.
An upload with no response keeps every report. A pending report is dropped by the plan at the
retention instant. Outputs: delete and keep, each a list of ids.
7. The aggregators
The aggregators and the collector run telemetry-aggregation §5, §6 and §7 as written for a task of measure 5, with these additions.
7.1 Retention and the hold
Retention. An aggregator keeps the task, its batch buckets, its replay state and the HPKE
configurations the task file names until the task’s retention instant (§5.3), and accepts
reports sealed to those configurations until then. DAP §6.4.1 lets it delete a task’s data after
its task_interval, and §4.4.1 asks it to “continue to accept reports with old keys”; this sets
both bounds for a client that holds no newer configuration.
The hold. The leader MUST NOT finish a collection job until its clock is at or past its
batch interval’s end in seconds, (start + duration) · time_precision, plus hold_s; until
then the job is processing (DAP §3.1, §4.6.1).
- A report uploaded after its batch is collected is discarded: the leader MAY mark it
report_replayedat upload (DAP §4.4.2.2), and one that reaches aggregation isbatch_collected(§4.5.4.3). It is lost. - An early collection is the collector’s undercount. No reader can see when a collection was made, and nothing here claims otherwise.
7.2 Report extensions and the token
At DAP §4.5.3.4, after its input share decrypts, an aggregator refuses a report:
- with any public extension,
invalid_message; - of a catalogue without attest, with any private extension,
invalid_message(a type the task does not use: §4.5.3.4 step 4); - of a catalogue with attest, unless its private extensions are exactly one
wai_impression_token(0x5749), recorded asattestation_absent(invalid_messageon the wire); and unless its data reads as a token of type 0x0002 (ad-presentation §7.1), has achallenge_digestthat is the SHA-256 of the TokenChallenge {0x0002, the catalogue’s issuer, the redemption context R, an empty origin_info} in ad-presentation §7.2’s form, with R the SHA-256 ofwai:impression-report-attest(ASCII), 0x01, the task id and the report id; has atoken_key_idthat is the SHA-256 of one of the catalogue’skeys; and verifies under that key (ad-presentation §7.3: RSASSA-PSS with SHA-384 and a 48-byte salt), recorded asattestation_invalid(invalid_messageon the wire).
Each aggregator checks its own input share, so a token in one share alone is refused by the
other. attestation_absent and attestation_invalid are ad-presentation §11’s codes. Double
spending is the report-id replay check (DAP §2.3): RFC 9577 §2.1.1.2 says that for a non-empty
redemption context the double-spend state “only needs to be stored across the set of Origin
servers that will accept tokens with that redemption context”, and R names one task and one
report.
The codepoint. DAP §9.2.4 sets aside no range for unregistered use, and its registries are
Specification Required (DAP §9.2). wai_impression_token, 0x5749, is proposed, with this text as
its specification, and is not registered. An aggregator that does not know it refuses every
attested report (DAP §4.5.3.4 step 4): no count, never a false one.
7.3 What each aggregator rejected
An aggregator of a task of measure 5 signs its statement in version 2, stating rejected
(telemetry-aggregation §6.1): the reports of the batch it rejected at aggregation, so a reader
sees beside the count what did not count. Reports with no token, with one of another report’s id,
with one that does not verify, and with a measurement that does not check are among them. A
report the leader refuses never reaches the helper, and a token in one input share alone is
refused by the aggregator that lacks it (§7.2), so the two counts can differ, each aggregator
stating its own. Neither is held to the other, and neither is a count of presentations: a rejected report
says what an aggregator refused, never what a receiver saw.
A reader accepts a statement of version 1 of such a task, an earlier aggregator’s, beside a statement
of either version. It states no count, which a reader reports as null for that aggregator alone,
never as 0: the other aggregator’s count stands beside it (§8.1, §8.2).
8. Verification
8.1 A receipt of an impression task
Inputs: the receipt, the catalogue, and optionally the batch’s reports (each report’s id, time and task).
Steps, stopping at the first refusal, which is stated with its reason and place: the
catalogue reads (§4.3; catalogue_malformed, at catalogue). Then telemetry-aggregation §8.1
steps 1 to 11 as written, its step 2 being §5.3 here against the catalogue. Then:
- at
leader: the statements’ batch falls within thetask_interval(DAP §4.1.1), otherwisebatch_invalid(telemetry-aggregation’s code: a batch the task does not admit); - at
result: each element of the result is at most the report count, and the elements’ sum, as integers, is the report count, otherwisecount_mismatch. With L at most 4,095 and each element at most a u64 count, the sum fits in a u128.
Outcome: accepted, stating telemetry-aggregation §8.2’s fields (measure impressions, scope
the catalogue’s digest), rejected, each aggregator’s count or null for a statement of
version 1, which states none, and reports_checked, whether step 11 ran; or the first refusal.
8.2 The table
Inputs: the catalogue, the TaskConfiguration, and receipts in an order, each with its reports where they are given.
- The catalogue reads (
catalogue_malformed) and the task meets §5.3 against it; otherwise the table is refused with that reason. - Each receipt, in order, is verified by §8.1, and its
taskmust then be the given TaskConfiguration byte for byte (task_mismatchattask, telemetry-aggregation’s code). A refused receipt is stated with its reason and place and takes no further part. - Of the accepted receipts, those with equal digests are one. Two whose batch intervals share a
time unit are both set aside,
receipts_overlap: DAP collects a bucket once (DAP §2.3;batchOverlap), so an honest pair cannot exist, and a reader holds no ground to choose between them (carousel §6.2 step 4, holdings §5.3). With the receipts sorted by batch start, a receipt overlaps an earlier one when the greatest end before it is past its start, and a later one when the next start is before its end. - Each receipt left stands.
Outcome:
- for each receipt, in order: its digest where it reads, and its verdict (
stands,set-aside reason=…orrefuse reason=… at=…); for a standing one, its batch (startanddurationintime_precisionunits,from_msandto_ms),collectable_from_ms= ((start+duration) ·time_precision+hold_s) · 1000, the earliest instant the hold allows its collection,report_count,rejected, each aggregator’s,reports_checked, and its rows; - totals: the rows summed over the standing receipts, each counted once, with the list of
their batch intervals, each aggregator’s
rejectedsummed over them,nullfor an aggregator when one of them states none, and whether every one’s reports were checked. A receipt given more than once is checked when any of its occurrences was: they are the same bytes, and §8.1 accepted the one given with its reports, so the order in which they arrive changes nothing. Each total is the exact integer sum. The standing batches share no time unit, so there are fewer than 2^27 of them (§5.3 bounds the retention instant), and each element is at most a u64 count (§8.1 step 13): a total is below 2^91 and fits in a u128, and a reader MUST NOT wrap or saturate it at 2^64. Arejectedtotal is bounded the same way. When none stands, the totals are empty: no row, never a row of zeros, and eachrejectednull.
A row is a (creative index, column) pair, for every creative and column in the catalogue’s
order, other last. It states presentations, the sum of its five cells; quartiles, five
counts, those at quartile at least 1, 2, 3, 4 and 5, as stitched-presentation §7.4 states them;
and floor_met, whether presentations is at least 50. Every row is stated: a 0 is a counted
zero of a collected batch, never an unknown.
8.3 What a table states
- An accepted receipt means that two keys signed shares whose sum is the result, over the
reports of the batch the task’s aggregators named (telemetry-aggregation §8.2), with
reports_checkedsaying whether the reports’ ids, times and checksum were held to it. - A row counts the presentations that receivers reported and that reached the leader before the collection: a lower bound. It does not count receivers, people or sessions; it is not an audience or a reach (opportunity-report §10.3: “no record of this text states an audience”); and it is never the n of bearer-receipts §7, where a larger n lowers the bound per receiver and would read an unknown audience as the optimum.
- The time resolution is the batch, never finer than
time_precision. A sparse hour is collected inside a wider batch (DAP §6.3). - A
rejectedcount is the word of the aggregator that signed it (§7.3), and states what it refused. It adds nothing to a row, and a row is not corrected by it. - A party that publishes a table beyond the holders of its receipts withholds every row whose
floor_metis false (delivery-telemetry §8, its §8.4: “suppressed, not rounded”) and publishes no figure that is a sum over rows that include a withheld one: a total row, a receipt’sreport_count, or a sum over creatives or regions.
9. Class, money and composition
- A class of its own. A table’s counts are the class aggregated: never summed into
revenue-share §5.2’s presented, beacons or unplaced, and never billable
under receipt-ledger §4.1’s
presented_impressionsorbillable_impressions. A tariff of version 7 names their own unit,aggregated_impressions, at a price of its own, and bills a receipt’s count by it, once in a chain (receipt-ledger §4.11). This is settlement §4.2’s rule for its unacknowledged class: “never billable as acknowledged”. A price list of rung-price bills delivery by the units of its §4.3 and takes no impression. - Never both. A receiver whose claim of a presentation leaves the device (stitched-presentation, stream-audit §6.3) makes no report of it, and one that reports it sends no claim of it.
- No energy. A table states no energy. A transmission’s energy is bearer-receipts §7’s, and its bound never divides by a table count.
- carousel. The task file, the key-log revisions, the pod-input records, the manifests and the creatives’ objects ride as objects of groups on a carousel. A publisher SHOULD schedule the task file’s group so that carousel §6.4’s “the group is held by t” holds with t at least the time its receivers take to fill a token pool before the flight starts, and the creatives’ and records’ groups by the flight’s start. That is the checkable promise that receivers within a loss bound can report attested presentations from the first hour. Nothing in a count depends on the schedule.
- timeliness. A creative’s deadline to be on a receiver before its break is a timeliness
entry of the creative’s object, with
by_msno later than the splice (timeliness §8.5). A publisher MAY also state one for the task file’s object, itsby_msthe flight’s start less the time its receivers take to fill a pool, so that carousel §6.4 at thatby_msis the promise of the bullet above. A count depends on neither. - holdings. A receiver that missed the task file or a creative fetches it from a neighbour whose holdings record lists its group (holdings §5.4) and verifies it as any object (holdings §6). The task file still needs its signer confirmed (§5.4). A neighbour with a path back is the natural relay.
- cache-authority. A peer that served a creative is not a party to its count, and a tally of the keys that held a creative by its deadline (cache-authority §8) counts holding, never presentation: the two are never summed. Both write regions in rung-price §2’s codes.
- bearer-receipts. An upload file travels as a reconciliation does (§6.4). A reconciliation attests receipt and a report counts a presentation: never the same fact, never merged. Key logs on the bearer are bearer-receipts §8 (its §8.2): held, anchoring nothing, carrying revocations.
- unidirectional-delivery. The contribution is a sink-evaluated, total, deterministic function (unidirectional-delivery §2), and the task authority is anchored out of band (§3 there).
- age-assurance. age-assurance §6 (its §6.1 to §6.3) holds device-held type-0x0002 tokens on a one-way bearer in the same way; here the challenge is per report, not per device.
- live-rooms. A binary record whose signer must hold a named authority in an origin’s key
log is live-rooms §5’s
live/roomand capture-binding §6 (its §6.4)capture/store;aggregate/taskis a third.
10. Reasons
The codes this extension adds, each with where it is raised:
| reason | raised at |
|---|---|
catalogue_malformed | §4.3, §5.4 step 2, §8.1, §8.2 step 1 |
catalogue_mismatch | §5.3 step 2 |
task_file_malformed | §5.4 step 1 |
record_not_shared | §6.2 step 2 |
record_terms_differ | §6.2 step 3 |
creative_not_listed | §6.2 step 5 |
instant_unknown | §6.2 step 6 |
outside_task | §6.2 step 7 |
token_exhausted | §6.2 step 8 |
upload_malformed | §6.4 |
receipts_overlap | §8.2 step 3 |
This extension also raises: telemetry-aggregation §11’s codes, task_invalid,
measure_mismatch, floor_too_low and aggregator_alone at §5.3, every code of its §8.1 at
§8.1, batch_invalid also at §8.1 step 12, and task_mismatch also at §8.2 step 2;
age-assurance §7.3’s count_mismatch (a receipt’s counts are not the counts they must be) at
§8.1 step 13; truncated, malformed, unknown_version, bad_key and bad_signature;
jwp-receipts §7.7’s confirmation reasons at §5.4 step 5; stitched-presentation §9’s codes and the
ad-presentation codes it reuses, at §6.2 steps 1 and 4; ad-presentation’s attestation_absent
and attestation_invalid at §7.2; and DAP’s report errors (DAP §4.1) and problem types (DAP
§3.6), by DAP’s names, at §6.6 and §7.
11. Conformance
live-conformance/impressions/ holds the corpus, written
from fixed seeds by wai_impression_aggregate_vectors gen as vectors.json. Its regions are
ISO 3166-1 user-assigned codes and their subdivisions, its origins reserved example domains, and
its issuer key RFC 9578’s published test key. It has:
- Catalogues: the flight’s and the open one’s, the bounds read at their edge (cells, regions, signers, keys, the hold), and a control for each rule of §4.3 and each way the layout breaks.
- Tasks: accepted ones, with the retention instant at the last instant exactly, and a control for each step of §5.3, an overflowing retention instant among them.
- Task files: accepted ones, one whose lists name another suite first; a control for each
way a file is malformed; and the confirmation’s refusals under the key logs held: a signer of
an unanchored origin, a key with
deliveryalone, a fetched anchor, a key revoked by a revision received on the bearer, and the same key accepted before that revision is held. - Contributions: each quartile at its mark, both carriages, every form of region, the hour, flight and deadline edges, a pool whose least pair fails, a catalogue without attestation, and a control for each step of §6.2.
- Flows, end to end, in telemetry-aggregation’s flow form with the uploads and token outcomes: an attested flight whose reports go through two overlapping relays, directly and through a relay that merges two receivers’ files; each kind of report §7.2 and DAP refuse; a report the helper committed sent to it again, which it refuses and does not count (§7.3); a collection held, then completed; a batch below the floor refused by both aggregators; and a report uploaded after its batch’s collection. A flight without attestation, the flight’s catalogue under another task, and a batch collected past the task’s interval.
- Uploads, plans and settlements: files that read and a control for each rule of §6.4; plans at each edge of the deadline, the retention instant and the budget; settlement of every DAP report error, a value DAP does not define, and responses that settle nothing.
- Receipts (§8.1) accepted with and without their reports, with the two aggregators stating
different
rejectedcounts in each order, with statements of version 1 and with a leader’s of version 2 beside a helper’s of version 1, and refused at each step this text adds, at the telemetry steps a receipt of another catalogue or task meets, for a statement of a version not defined, and for a helper’s count changed after it signed; tables (§8.2) with a receipt given twice, a receipt given with and without its reports in both orders, overlapping receipts, a receipt of statements of no count, a receipt of one statement of no count, receipts of another task and catalogue, and the table’s own refusals.
A conforming verifier gives every case the outcome, the reason and everything else the corpus
states. wai_impression_aggregate_vectors verify and the test tests/impression_aggregate.rs
check that the file regenerates byte for byte, that wai-rs gives every case its outcome, that
every reason of §10 appears, and that §10 lists exactly the reference’s codes. The test also
checks, without storing it, that each standing receipt’s result is the plaintext tally of the
cells of the contributions its reports came from, and that stitched-presentation’s own
verification of the flight’s claims, step 11 left out, gives per creative the presentations and
quartile counts the flight’s table gives summed over its columns. The size bounds of §5.4 and
§6.4 are checked in code, since no vector carries them: a file of exactly the bound that reads in
every other respect is read, a task file then reaching step 2, and the same file one byte longer
is refused at step 1. §4.3’s bound decides no outcome (§4.3), so no check can observe it.
live-conformance/impression_aggregate_verify.py is the second implementation, written from this
text: it shares no code with wai-rs, evaluates Prio3 and opens every report with the QoE
corpus’s second implementation, judges records and claims with stitched-presentation’s, verifies
tokens with ad-presentation’s, confirms task-file signers with the signer corpus’s, writes the
catalogue, the task file, the upload file, the contribution, the plan, the settlement and the
table itself, requires every mutated copy of the corpus to fail, and drops each of its own checks
in turn.
12. Security and privacy considerations
- What the source learns. Reporting gives up part of unidirectional-delivery §2’s “the source learns nothing”: the collector learns batch sums over at least 50 presentations; each aggregator learns each report’s id, time bucket, length, task, validity and arrival; relays learn the same, less validity; the issuer learns when a device refilled its pool.
- Non-collusion (telemetry-aggregation §13). The receipt names three keys and checks that the aggregators’ differ. Whether two operators pool their shares is in no byte.
- Who may define a task. Only a key that an origin’s pinned key log lists with
aggregate/task. A task decides which two aggregators receive every receiver’s shares, so a fetched anchor, authority taken on first use, confirms none. A key-log revision on the bearer revokes a task key on receivers with no path (bearer-receipts §8.2). A key compromised and not yet revoked can name colluding aggregators for new tasks: the bound is the origin’s revocation. - Sybil. Without attest, anyone holding a task file submits reports: the count is of reports, not of presentations. A deployment that bills SHOULD set attest, as stitched-presentation §4 (its §4.6) says of shared breaks. With attest, each counted report costs a token issued to a device its attester vouched for, and the bound per device is the issuer’s issuance policy. A farm of genuine devices attests truthfully (ad-presentation §13).
- The local verdict is the receiver’s word (unidirectional-delivery §2: determinism, not enforcement). A modified receiver reports any cell in range; the range proof stops only values out of range (DAP §2.2; telemetry-aggregation §13).
- The aggregators’ word.
count_mismatchcatches a share that changes the integer sum or wraps an element. It does not catch a share that moves counts between two cells that both stay in range. A leader that holds a report’s token (its private extension) can drop that report and submit one of its own making under the same id and token, moving that presentation to another cell. Neither adds a presentation. A correct result needs both aggregators honest (DAP §8; telemetry-aggregation §8.2). - Small cells. The batch floor of each aggregator, regions no finer than ISO 3166-2 and buckets of at least an hour bound what a cell reveals. A collector that inserts reports it knows shrinks a batch’s effective set (DAP §8.1); attestation raises that cost. Differential privacy (DAP §8.5) is not used, because these counts are billed, and noise in a billed figure is not deterministic.
- Linking. Reports of one receiver uploaded together can be linked by their arrival, though not by their values; relays that merge files, and Oblivious HTTP (DAP §8.4), break that. Tokens are unlinkable to their issuance (RFC 9576 §3.3); a refill is a timing fact the issuer sees.
- Lateness. A receiver that never returns is never counted. The direction is always under.
- No consent rule (telemetry-aggregation §10). Where a regime asks, a
lawful-basisclaim (jwp-receipts §2.4.6) states the basis. - Bounded work. A catalogue is at most 1 MiB, a task file 1 MiB and an upload file 16 MiB, each read once. A contribution is one stitched verdict and lookups; a task file’s confirmation is jwp-receipts §7.4’s; a table is linear in receipts times L, plus a sort of the receipts.