open-standards/wai/extensions/telemetry-aggregation.md
Transaction Science 61320c8641 A report an aggregator committed and was sent again is not rejected, and a reader takes one statement of no count
telemetry-aggregation §6.1's rule that `rejected` counts no report the aggregator aggregated had no
test, and impression_aggregate_verify.py did not apply it. The flight flow now sends the helper a
report it committed, and the Python flow filters as the Rust does. impression-aggregates §7.3 says
what a reader does with a statement of version 1 beside one of version 2, and the corpus adds a
receipt and a table of each.

The ledger's corpus takes these receipts, so it is regenerated by the commit after this one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 08:35:01 -05:00

29 KiB
Raw Permalink Blame History

WAI Extension: QoE Aggregation

Status: Draft. Registers the record wai.telemetry.aggregate: quality-of-experience figures the service receives only as sums. A sink reports each session's measures, the session metrics of delivery-telemetry §7 (its §7.2) cast as Prio3 measurements, through the Distributed Aggregation Protocol of draft-ietf-ppm-dap-20 (DAP): it secret-shares each measurement between a leader and a helper aggregator, and encrypts each share to its aggregator. The aggregators check each measurement with the range proof of its Prio3 VDAF (draft-irtf-cfrg-vdaf-22) without either of them seeing it, sum the valid ones, and release an aggregate share only for a batch of at least the task's minimum size, which this extension never lets fall below fifty reports. Each aggregator signs a statement of its aggregate share with the batch's parameters (WQS1), and the collector, the service, signs an aggregate receipt (WQA1) that carries the task, both statements and the result: a reader recomputes the result from the two shares and knows what was summed, over how many reports, and by whom. With two aggregators, neither learns a measurement as long as one of them follows the protocol. A room's quality-of-experience cell (live-metering §4.7) is computed the same way. Leader and helper are roles any party can run. No new transport: DAP runs over HTTP, as its own text defines. The reference implementation is the telemetry_aggregate module of wai-rs (feature telemetry_aggregate), with a reference leader, helper and collector that run in its tests; the corpus is live-conformance/aggregate/, and its second implementation is live-conformance/telemetry_aggregate_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.

Texts read for this revision: draft-ietf-ppm-dap-20 (all of it; §4 to §8 and Appendix B are used here) and draft-irtf-cfrg-vdaf-22 (§4 to §7, §9, Appendices A to C), both Internet-Drafts, with the test vectors the VDAF draft's [TestVectors] reference names; RFC 9180 (§4, §5.1, §6.1, §7.1, Appendix A.1); RFC 9861 (§2.1, §5); RFC 5869; RFC 8032; FIPS 180-4; CTA-5004-B through delivery-telemetry. A section number of a draft in this text is that revision's.

1. Scope

In scope: the measures and the measurement each takes from a session's metrics (§3); the DAP task a measure runs under, the WAI layout of its task_info, its id and the rules a task meets (§4); the roles of DAP as this extension uses them (§5); the statement an aggregator signs (§6); the aggregate receipt (§7); its verification (§8); where aggregation replaces the suppression of delivery-telemetry §8 (its §8.4), where that suppression still applies, and a room's cell (§9).

Out of scope: running aggregators in production; how a deployment chooses its aggregators and agrees a task with them, which DAP leaves out of band (draft-ietf-ppm-dap-20 §4.2); client authentication and anonymizing proxies (DAP §8.3, §8.4), except that a task of measure 5 authenticates its reports by the token of impression-aggregates §7.2; differential privacy (DAP §8.5); any VDAF but the four Prio3 variants of §3.

Relationship to the core spec: wai.telemetry.aggregate 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

  • DAP, task, report, report share, input share, leader, helper, collector, batch, batch bucket, batch interval, minimum batch size, aggregate share, aggregate result: as draft-ietf-ppm-dap-20 §1.2.1 defines them. This extension's collector is the content's own service.
  • Prio3, measurement, verification, verifier share: as draft-irtf-cfrg-vdaf-22 defines them (§5, §7).
  • Session metrics: the figures delivery-telemetry §7 computes from one session's log (its §7.2).
  • Measure: a quantity of §3, aggregated by one task.
  • Statement: an aggregator's signed account of the aggregate share it released (§6).
  • Aggregate receipt: the collector's signed record of an aggregate result (§7).

3. Measures

§3.1 A measure takes one figure, or a few, from a session's metrics and aggregates them with one Prio3 variant of draft-irtf-cfrg-vdaf-22 §7.4, with two aggregators and one proof:

code measure VDAF (§7.4 there) configuration (DAP Appendix B) measurement, from the session's metrics
1 outcomes Prio3SumVec length 4, max_measurement 1, chunk_length 2 [started, start_failed, exit_before_start, vst_ms ≠ null], each 0 or 1
2 stalls Prio3Sum max_measurement 65535 stalls, at most 65535
3 durations Prio3SumVec length 3, max_measurement 4294967295, chunk_length 10 [stall_ms, engaged_ms, vst_ms], vst_ms 0 when null, each at most 4294967295
4 start_time Prio3Histogram length 8, chunk_length 3 the bucket of vst_ms: 0 to 5 for a time below 250, 500, 1000, 2000, 4000 and 8000 ms, the first that holds; 6 for a later start; 7 for null
5 impressions Prio3Histogram length L and chunk_length ⌈√L⌉ of the catalogue whose digest is scope (impression-aggregates §4, §5) not a session's: one presentation's cell (impression-aggregates §6.2)

§3.2 A figure past its measure's largest value is reported as that value, and a sum counts it at that value. Each largest value is past what one session's figures reach in practice: a stall count of 65535, and 49 days in milliseconds.

§3.3 The range proof is the VDAF's: Prio3Sum and Prio3SumVec encode each value as the bits of draft-irtf-cfrg-vdaf-22 §7.4.2 and prove each bit is 0 or 1, so a value past the largest cannot be encoded, and Prio3Histogram proves its vector has one 1 and every other element 0 (§7.4.4 there). An aggregator that verifies a report whose measurement is out of range finds the proof fails and rejects the report, vdaf_verify_error (DAP §4.5.3.2), without learning the measurement.

§3.4 Prio3Count (VDAF §7.4.1) is implemented with the others and passes the draft's vectors; no measure of this revision uses it.

4. The task

4.1 task_info

A measure runs under one DAP task (draft-ietf-ppm-dap-20 §4.2). Its configuration's task_info, which DAP leaves to the deployment, is laid out as follows, 153 bytes:

field size meaning
label 8 wai-qoe1 (ASCII)
measure 1 the measure's code (§3.1)
scope 32 32 zero bytes for a task of the service's own; the room id for a room's (§9.2); the digest of its catalogue for a task of measure 5 (impression-aggregates §5.1)
salt 16 chosen by the task's author, so that two tasks of one configuration have two ids
leader 32 the Ed25519 key the leader signs its statements with
helper 32 the helper's
collector 32 the collector's, which signs the aggregate receipt

The task configuration enters the authenticated data of every input share and of every aggregate share (DAP §4.4.2.1, §4.6.7), so a report is bound at upload to the measure, the scope and the three keys, and an aggregator that holds another configuration cannot decrypt it.

4.2 The task id

The task id is SHA-256 of the 29 bytes wai:telemetry-aggregate/task (ASCII) and 0x00, followed by the TaskConfiguration's encoding. DAP admits a task id derived from the task's parameters (§8.6.1 there); the salt makes it unpredictable.

4.3 Rules

A task configuration is refused, in this order:

  1. task_invalid when it does not read as a TaskConfiguration; its task_info is not 153 bytes beginning wai-qoe1; its time_precision is 0; its batch_mode is not time-interval (1) or its batch_config is not empty; or an extension is other than task_interval (1) holding one Interval, or the extensions are not in ascending order of type;
  2. measure_unknown when measure is no code of §3.1;
  3. measure_mismatch when vdaf_type and vdaf_configuration are not the measure's: its VDAF identifier (VDAF Table 19) and its DAP Appendix B encoding. For measure 5, this rule is impression-aggregates §5.3 steps 2 to 4 against the catalogue whose digest is scope; a verifier given no such catalogue refuses the task catalogue_mismatch;
  4. floor_too_low when min_batch_size is below 50, the floor of delivery-telemetry §8 (its §8.4);
  5. aggregator_alone when leader and helper are one key.

The time-interval batch mode is the only one, because a leader-selected batch is one the leader composes (DAP §8.2); a task's time_precision SHOULD be an hour or more, which also coarsens the report times an aggregator sees (DAP §8.4).

4.4 The verification key

The aggregators share the VDAF verification key of the task, kept from every client (DAP §8.6.2). The reference derives it as that section shows: HKDF-Expand of HKDF-Extract(verify_key, a seed the aggregators agree) with the task id. Its verification key id is 0.

5. The protocol

§5.1 The sink. When a session ends or is abandoned, the sink computes its metrics (delivery-telemetry §7.2) and, for each measure the service runs, a report: a fresh random report id, the time of the session's end or abandon in units of the task's time_precision, and the measurement of §3.1 sharded and encrypted as DAP §4.4.2.1 does, with the application context dap-20 followed by the task id. It uploads the reports to the leader (DAP §4.4.2). A session gives at most one report to each task. Its reports carry no sid, no principal and no extension. A task of measure 5 takes its reports from impression-aggregates §6.3 instead: one for each presentation, its time the presentation's, and with attestation one private extension.

§5.2 The aggregators. The leader and the helper run DAP §4.5 and §4.6 as written, over HTTP, with the HPKE suite of DAP §7 (DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM):

  • each decrypts its own input share, checks it (§4.5.3.3, §4.5.3.4) and runs Prio3's one-round verification with its peer by the ping-pong exchange of VDAF §5.7.1, so a report whose proof fails is rejected by both, vdaf_verify_error;
  • each commits a valid report's output share to the batch bucket of its time, once (report_replayed for a second), and keeps per bucket the aggregate share, the count and the checksum, the XOR of the SHA-256 of each report id (§4.5.4.3);
  • each refuses to release an aggregate share for a batch below the task's minimum batch size: the leader fails the collection job and the helper the aggregate share request, invalidBatchSize (§4.6.1, §4.6.4); the helper also refuses a report count or checksum not its own, batchMismatch, and a batch already collected, batchOverlap;
  • each encrypts its aggregate share to the collector (§4.6.7) and signs a statement of it (§6).

A batch below the minimum therefore yields no aggregate as long as one aggregator follows the protocol: the result needs both shares.

§5.3 The collector. The collector opens both aggregate shares and both statements, requires each statement's share and count to be the ones DAP delivered, unshards the shares (VDAF §7.2.5) and signs the aggregate receipt (§7).

§5.4 What each party learns. The leader and the helper each hold one share of each measurement: a share alone, and the verification exchange, reveal nothing about it but whether it is valid (VDAF §7.1; DAP §2.2). Each sees the reports' ids, times and lengths and which reports were rejected. The collector, and a reader of a receipt, learn the aggregate, its report count and its batch, nothing per session. Two aggregators that pool their shares learn every measurement: the deployment chooses aggregators that do not, and DAP §8.7 says why their infrastructure should not be shared.

6. The aggregate share statement

6.1 Layout

When an aggregator releases its aggregate share for a batch, it signs a statement, WQS1:

field size meaning
magic 4 WQS1 (ASCII)
version 1 1, or 2 for a statement that states rejected
role 1 2 for the leader, 3 for the helper (DAP §4.1)
task_id 32 the task id (§4.2)
collection 32 SHA-256 of the CollectionJobReq the share answers (DAP §4.6.1)
batch 16 the batch: its interval, start and duration (u64 each) in units of the task's time_precision
report_count 8 u64: the reports aggregated in the batch
checksum 32 the batch's checksum (DAP §4.5.4.3)
rejected 8, in version 1 absent version 2: u64, the reports this aggregator rejected, below
agg_share 4 + n u32 length, then the aggregator's aggregate share, encoded as VDAF §7.2.7.5 encodes it
signer 32 the aggregator's key, the one task_info names for its role
sig 64 Ed25519 by signer over the 30 bytes wai:telemetry-aggregate/share (ASCII) and 0x01, followed by every byte before sig, verified strictly (SPEC §7 Signatures)

Integers are big-endian. A statement is read exactly: its magic, version 1 or 2, a role of 2 or 3, and no byte past its end; otherwise it is refused statement_invalid.

rejected is the number of distinct reports, each with a time in the batch and none of them aggregated, that this aggregator rejected with a report error during aggregation (DAP §4.5.3.4, §4.5.4.3): a report it committed and was sent again is one it aggregated, and is not counted. A leader counts every report of an aggregation job that did not commit, the ones its helper rejected among them; a helper counts the ones it was sent and rejected itself, and never sees one the leader refused first. So each aggregator states its own count and the two need not agree. A report refused at upload (DAP §4.4.2.2) reached no aggregation and is counted by neither. The count is that aggregator's word: no verifier recomputes it, since a rejected report leaves no trace in report_count, checksum or the share. The signature covers it, so a statement with its count changed is refused bad_signature.

6.2 When an aggregator signs

An aggregator signs a statement only for a batch whose aggregate share it releases under DAP §4.6: a task that meets §4.3, a batch of at least the minimum batch size, and, at the helper, a report count and checksum that are its own. Its batch is the batch selector's interval, report_count and checksum are its batch's, and agg_share is the share it encrypted to the collector. An aggregator of a task of measure 5 signs version 2 (impression-aggregates §7.3); of a task of another measure it signs version 1 or 2.

6.3 How a statement travels

A statement carries an aggregate share in clear, so it travels as the share does: sealed to the collector with SealBase (RFC 9180 §6.1) under the collector's HPKE configuration, with the info string wai-telemetry-aggregate statement (ASCII) followed by the sender's role and the collector's (0x00), and the AggregateShareAad of DAP §4.6.7 as authenticated data. The leader that relays the helper's learns nothing from it. How the sealed statement reaches the collector beside DAP's own messages is the deployment's.

7. The aggregate receipt

7.1 Layout

The collector signs the aggregate receipt, WQA1:

field size meaning
magic 4 WQA1 (ASCII)
version 1 1
task 2 + n u16 length, then the TaskConfiguration (DAP §4.2)
collection 4 + n u32 length, then the CollectionJobReq (DAP §4.6.1)
interval 16 the CollectionJobResp's interval: the smallest interval holding the batch's reports
leader 4 + n u32 length, then the leader's statement
helper 4 + n u32 length, then the helper's statement
result 2 + 16 × k u16 count, then each element of the aggregate result as a u128
signer 32 the collector's key, the one task_info names
sig 64 Ed25519 by signer over the 32 bytes wai:telemetry-aggregate/receipt (ASCII) and 0x01, followed by every byte before sig, verified strictly

The receipt's digest is SHA-256 over every byte before sig.

7.2 Reading

A reader reads exactly the bytes it is given: truncated when they end before a field does, malformed for another magic or a byte past sig, and unknown_version for a version other than 1. A helper of length zero reads, and is refused at §8.1 step 5.

8. Verification

8.1 Steps

A verifier is given the receipt and, where it has them, the batch's reports: each report's id, its time and the task it was uploaded to. It runs these steps and stops at the first refusal, which it reports with the reason and where:

  1. The receipt reads (§7.2), at receipt.
  2. The task reads as a TaskConfiguration and meets §4.3, at task: task_invalid, measure_unknown, measure_mismatch, floor_too_low, aggregator_alone. A task of measure 5 is verified by impression-aggregates §8.1, which is given its catalogue.
  3. The collector: signer is the collector key of task_info, otherwise wrong_signer; the signature verifies (bad_key, bad_signature), at receipt.
  4. The collection reads as a CollectionJobReq with a time-interval query of a duration of at least 1, an empty aggregation parameter and no extension (Prio3's only aggregation parameter is the empty one, VDAF §7.2.3): otherwise collection_invalid, at collection.
  5. The statements: both are present, and their signers differ and their roles differ, otherwise aggregator_alone; each reads (§6.1, statement_invalid); the first has role 2 and the leader's key and the second role 3 and the helper's, otherwise wrong_signer; each signature verifies (bad_key, bad_signature), at leader or helper.
  6. The binding: each statement's task_id is the task's id (§4.2) and its collection the SHA-256 of the receipt's collection, otherwise task_mismatch.
  7. The batch: each statement's batch has a duration of at least 1 and falls within the query's interval, and the receipt's interval has a duration of at least 1 and falls within the batch (DAP §4.1.1), otherwise batch_invalid.
  8. Agreement: the two statements state one batch, one report count and one checksum, otherwise statements_disagree. Their rejected counts are each aggregator's own, and are not compared.
  9. The floor: the report count is at least the task's min_batch_size, otherwise batch_too_small.
  10. The result: each agg_share is an aggregate share of the task's VDAF (its length, and each element below the field's modulus), otherwise share_invalid; the result is the two shares unsharded (VDAF §7.2.5), otherwise result_mismatch.
  11. The reports, where given: each was uploaded to the receipt's task, each id appears once, each time falls within interval, and their number and checksum are the statements', otherwise batch_mismatch, at reports.

8.2 Outcome

A receipt that passes is accepted: its digest; the task id, the measure, the VDAF and its configuration, the scope, the time precision and the minimum batch size; the batch and the interval; the report count; the result; the rejected of each statement of version 2, by its aggregator; and the leader's, the helper's and the collector's keys. What was summed is the measure's VDAF over that task's reports, over the report count the two aggregators each signed, by the aggregators the task named.

An accepted receipt shows that two keys each signed its share of the batch and that the result is their sum. It does not show that the aggregators ran verification honestly: DAP assures a correct result only when both do (DAP §8). A statement makes each aggregator's part attributable to its key.

9. Where aggregation replaces suppression

9.1 Event and stream reporting

delivery-telemetry §8 suppresses a published cell of fewer than 50 sessions (its §8.4); the service holds each session's observations and refrains. Under this extension the service never holds a session's measures: it holds aggregates, each over at least the task's minimum batch size, enforced by each aggregator (§5.2) and checked by every reader (§8.1 step 9). For a figure a measure of §3 aggregates, a deployment MAY report it by DAP instead of in an Event-mode report, and the published figure is the receipt's.

§8.4's suppression still applies where reporting is a stream rather than a sum after the fact:

  • Request-mode reports, which travel with each request to the host that serves it (delivery-telemetry §8.3): a host acts on them per request, and a deployment that keeps them to the delivery class does so with delivery-only Request mode (delivery-telemetry §4.3);
  • Event-mode reports a service consumes as they arrive, for one session's playback while it plays, which no batch can wait for;
  • a figure no measure of §3 aggregates.

9.2 A room's cell

A room's clients MAY report their sessions' quality of experience under this extension instead of in the room's logs document (live-metering §6.1). The room's outcomes, stalls and durations tasks have the room id as their scope. Over one batch, the cell of live-metering §4.7 is, from the three accepted receipts of that batch and scope:

cell field from
started, start_failed, exit_before_start, vst_count outcomes, elements 0 to 3
stalls stalls
stall_ms, engaged_ms, vst_ms durations, elements 0 to 2

each with the report count of its receipt. A set of receipts that is not one of each of the three measures, or that names more than one scope, a zero scope, or more than one batch, is refused cell_mismatch. Within the clamps of §3.2 these are the figures live-metering §4.7 computes from the same sessions' logs. The room-session receipt then names no logs (qoe zero) and its cell is suppressed, as that section requires of a receipt with fewer than 50 logs; the room's cell is the aggregate receipts', whose floor the aggregators enforce.

10. What a report reveals

The key classes of delivery-telemetry §3 (its §3.4) state, for each key of the CMCD mapping, what it reveals and to whom. A DAP report reveals to each aggregator its id, its time, its length, which task it belongs to and whether its proof verified, and to no party its measurement (§5.4). This extension sets no consent requirement for the service's own mechanics: using a distributor's WAI service is the person's agreement to how that service works, its aggregation among them. Where a regime asks for a lawful basis for processing quality-of-experience data, the service records it in a lawful-basis claim (jwp-receipts §2.4.6).

11. Reasons

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

reason raised at
task_invalid §4.3, §8.1 step 2
measure_unknown §4.3, §8.1 step 2
measure_mismatch §4.3, §8.1 step 2
floor_too_low §4.3, §8.1 step 2
wrong_signer §8.1 steps 3 and 5
collection_invalid §8.1 step 4
statement_invalid §6.1, §8.1 step 5
aggregator_alone §4.3, §8.1 step 5
task_mismatch §8.1 step 6
batch_invalid §8.1 step 7
statements_disagree §8.1 step 8
batch_too_small §8.1 step 9
share_invalid §8.1 step 10
result_mismatch §8.1 step 10
batch_mismatch §8.1 step 11
cell_mismatch §9.2
measurement_key_in_delivery delivery-telemetry §4 (its §4.3)

A verifier of this extension also raises truncated, malformed and unknown_version for a receipt that does not read, and bad_key and bad_signature for its signatures. The reference aggregators raise DAP's report errors (draft-ietf-ppm-dap-20 §4.1: among them vdaf_verify_error, hpke_decrypt_error, report_replayed) and its problem types (§3.6 there: among them invalidBatchSize, batchMismatch, batchOverlap, batchInvalid), as DAP names them.

12. Conformance

live-conformance/aggregate/ holds:

  • vdaf-22/: the Prio3Count, Prio3Sum, Prio3SumVec and Prio3Histogram test vectors and the XofTurboShake128 vector of draft-irtf-cfrg-vdaf-22 Appendix C, as published at the commit its [TestVectors] reference names;
  • vectors.json, written from fixed seeds by wai_telemetry_aggregate_vectors gen:
    • the keys: each aggregator's and the collector's HPKE configuration and private key, the three statement keys and a stranger's, and the seed of the verification keys (test material from fixed seeds, as every corpus here);
    • fifty sessions' metrics, and a flow for each measure: its task, the reports (fifty sessions, one with an out-of-range measurement, and in one flow a report replayed and in another one whose helper share does not decrypt), the upload request and the reports it refused, the aggregation job's request and response with each report's outcome, the collection request, the aggregate share request, the helper's aggregate share, the collection response, both sealed statements and the result; the outcomes flow also holds the collections each aggregator refuses (a batch below the minimum, a checksum not the batch's, a batch past the query, a batch collected twice);
    • receipts: the four flows' receipts, accepted, and refused ones, each a control of one defect: every reason of §11 the receipt raises, the reading and signature codes, a receipt signed for a task the reports were not uploaded to, a batch below the minimum, a leader alone and a leader signing as both aggregators;
    • cells: the room's cell from its three receipts, with the plaintext cell of live-metering §4.7 over the same sessions, which it equals, and a set of receipts of two scopes;
    • delivery-only reports (delivery-telemetry §4.3): observations with their delivery-only reports and header shards, and reports that carry a key past the delivery class.

Statements of version 2 are in the corpus of impression-aggregates §11, whose aggregators sign them.

A conforming verifier gives every vector its outcome. wai_telemetry_aggregate_vectors verify and the test tests/telemetry_aggregate.rs check that vectors.json regenerates byte for byte, that vdaf-22/ is the files as published and that wai-rs passes every one, that every receipt, cell and delivery-only report gets its outcome, that every reason of §11 and every reused code refuses a vector, that §11 lists exactly the reference's codes, and that delivery-telemetry §3 (its §3.4) classes exactly the keys the reference does. live-conformance/telemetry_aggregate_verify.py is the second implementation, written from this text and the drafts it cites: it shares no code with wai-rs; it evaluates Prio3 itself (the fields, TurboSHAKE128 and the XOF, the FLP in the Lagrange basis) and passes the draft's vectors; it opens every report with its own HPKE (X25519, HKDF-SHA256 and AES-128-GCM written from RFC 7748, RFC 5869 and the AES and GCM specifications), recombines each measurement and requires it to be its session's, runs both aggregators' verification and requires the aggregation job's request and response byte for byte, recomputes every batch bucket, every refusal, every aggregate share and every result; verifies every receipt and cell with WAI's strict Ed25519 rule (claim-conformance/_ed25519.py); reads the key classes from delivery-telemetry §3 (its §3.4) and this text's reasons from §11; and requires every mutated copy of the corpus to fail.

13. Security and privacy considerations

  • Non-collusion. Privacy rests on the two aggregators not pooling their shares (§5.4). A statement attributes each share to a key, so a reader sees which two parties a result depended on; it cannot see whether they colluded.
  • Correctness. The range proof stops a sink from poisoning a sum with an out-of-range value (§3.3); it does not stop a sink from reporting a false value in range (DAP §2.2). An aggregator can still corrupt a result by an arbitrary amount with a wrong share; the statement makes that share its signed statement.
  • Small batches. A batch at the floor reveals what fifty sessions sum to. A deployment that publishes overlapping figures across tasks of one population should consider what their differences reveal; DAP collects each batch once (§2.3 there), which this extension keeps, and §8.5 there describes noise an aggregator can add.
  • Linking a session's reports. A session sends one report to each task, at once; an aggregator that sees their arrival together can link them, though not their values. A deployment that wants them apart sends them at different times or through a proxy (DAP §8.4).
  • Sybil reports. Reports are not authenticated here; one party can submit many, and so approach a batch's floor with its own (DAP §8.1). The floor is a floor of reports, not of people.
  • Verification key. A sink that knew the verification key could forge a report that passes verification (DAP §8.6.2); it is the aggregators' alone.