<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.5 (Ruby 3.2.2) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-dcook-ppm-dap-interop-test-design-07" category="info" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.19.4 -->
  <front>
    <title>DAP Interoperation Test Design</title>
    <seriesInfo name="Internet-Draft" value="draft-dcook-ppm-dap-interop-test-design-07"/>
    <author fullname="David Cook">
      <organization>ISRG</organization>
      <address>
        <email>dcook@divviup.org</email>
      </address>
    </author>
    <date year="2024" month="February" day="28"/>
    <area>Security</area>
    <workgroup>Privacy Preserving Measurement</workgroup>
    <abstract>
      <?line 57?>

<t>This document defines a common test interface for implementations of the
Distributed Aggregation Protocol for Privacy Preserving Measurement (DAP) and
describes how this test interface can be used to perform interoperation testing
between the implementations. Tests are orchestrated with containers, and new
test-only APIs are introduced to provision DAP tasks and initiate processing.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://divergentdave.github.io/draft-dcook-ppm-dap-interop-test-design/draft-dcook-ppm-dap-interop-test-design.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-dcook-ppm-dap-interop-test-design/"/>.
      </t>
      <t>
        Discussion of this document takes place on the
        Privacy Preserving Measurement Working Group mailing list (<eref target="mailto:ppm@ietf.org"/>),
        which is archived at <eref target="https://mailarchive.ietf.org/arch/browse/ppm/"/>.
        Subscribe at <eref target="https://www.ietf.org/mailman/listinfo/ppm/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/divergentdave/draft-dcook-ppm-dap-interop-test-design"/>.</t>
    </note>
  </front>
  <middle>
    <?line 66?>

<section anchor="introduction">
      <name>Introduction</name>
      <t>This document defines a common test interface for implementations of the
Distributed Aggregation Protocol for Privacy Preserving Measurement <xref target="DAP"/>. This
test interface facilitates interoperation tests between different participating
DAP implementations. As DAP has four distinct protocol roles, (Client, Leader,
Helper, and Collector) manual interoperation testing between all combinations of
even a small number of DAP implementations could be taxing. The goal of this
document's common test interface is to enable automation of these interoperation
tests, so that different participating implementations can be exchanged for each
other, and the same test suite can be re-run on different combinations of
implementations. Simplifying interoperation testing will aid in identifying
errors in implementations, identifying ambiguities in the protocol
specification, and reducing regressions in implementations.</t>
      <t>Taking inspiration from QuicInteropRunner <xref target="SI2020"/>, each participating
implementation provides one or more container images adhering to a common
interface. A test runner will start one container for each protocol participant,
configure networking between the containers, and send various HTTP API requests.
As part of this common testing interface, the HTTP servers in the containers
will support some new test-only HTTP APIs, which will allow the test runner to
provision shared task parameters and secrets, as well as trigger the start of
different sub-protocols.</t>
    </section>
    <section anchor="conventions-and-definitions">
      <name>Conventions and Definitions</name>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
appear in all capitals, as shown here.</t>
      <?line -18?>

</section>
    <section anchor="container-interface">
      <name>Container Interface</name>
      <t>Each participating DAP implementation may provide one or more container images,
one for each protocol role it implements. (Client, Leader, Helper, and
Collector) A list of available container images will be maintained for each
role. Implementations may want to submit a single Aggregator image in both the
Leader list and Helper list. The test runner will fetch each container using the
given repository, image name, and tag.</t>
      <t>When the container's entry point executable is run, it <bcp14>SHALL</bcp14> start up an HTTP
server listening on port 8080. In all cases, the container will serve the
endpoints described in <xref target="test-api"/> (particularly, the subsection or
subsections appropriate to its protocol role). In the case of a Helper or
Leader container, it <bcp14>SHALL</bcp14> also serve the endpoints specified by <xref target="DAP"/> on a
port (which <bcp14>MAY</bcp14> be the same port 8080 as used to serve the interoperation test
API) at some relative path. The container should run indefinitely, and the test
runner will terminate the container on completion of the test case. (While DAP
requires HTTPS connections, only using HTTP between containers simplifies test
setup. Putting TLS client/server interop out-of-scope for these tests is
acceptable, as it's not of interest.)</t>
      <t>Log output <bcp14>SHOULD</bcp14> be captured into the directory "/logs" inside the container.
This will be copied out to the host for inspection on completion of the test
case.</t>
      <t>No environment variables or volume mounts will be provided to the containers.</t>
    </section>
    <section anchor="test-api">
      <name>Interoperation Test API</name>
      <t>Each container will have an HTTP server listening on port 8080 for commands from
the test runner. All requests <bcp14>MUST</bcp14> use the HTTP method POST. Requests and
responses for each endpoint listed below <bcp14>SHALL</bcp14> be encoded JSON objects
<xref target="RFC8729"/>, with media type <tt>application/json</tt>. All binary blobs (i.e. task
IDs, batch IDs, HPKE configurations, and VDAF verification keys) <bcp14>SHALL</bcp14> be
encoded as strings with base64url <xref target="RFC4648"/>, inside the JSON objects. Any
integer values in the parameters, measurement, or aggregate result of a <xref target="VDAF"/>
will be encoded as strings in base 10 instead of as numbers. This avoids
incompatibilities due to limitations on the range of JSON numbers that different
implementations can process.</t>
      <t>Each of these test APIs should return a status code of 200 OK if the command was
received, recognized, and parsed successfully, regardless of whether any
underlying DAP request succeeded or failed. The DAP-level success or failure
will be included in the test API response body. If a request is made to an
endpoint starting with "/internal/test/", but not listed here, a status code of
404 Not Found <bcp14>SHOULD</bcp14> be returned, to simplify the introduction of new test APIs.</t>
      <section anchor="common-structures">
        <name>Common Structures</name>
        <section anchor="vdaf">
          <name>VDAF</name>
          <t>In multiple APIs defined below, the test runner will send the name of a <xref target="VDAF"/>,
along with the parameters necessary to fully specify the VDAF. These will be
stored in a nested object, with the following attributes (new <tt>type</tt> values and
new keys will be added as new VDAFs are defined).</t>
          <table anchor="vdaf-object">
            <name>VDAF JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>type</tt></td>
                <td align="left">One of <tt>"Prio3Count"</tt>, <tt>"Prio3Histogram"</tt>, <tt>"Prio3Sum"</tt>, <tt>"Prio3SumVec"</tt>, or <tt>"Poplar1"</tt></td>
              </tr>
              <tr>
                <td align="left">
                  <tt>length</tt> (only present if <tt>type</tt> is <tt>"Prio3Histogram"</tt> or <tt>"Prio3SumVec"</tt>)</td>
                <td align="left">The length of the vectors being summed, encoded in base 10 as a string.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>chunk_length</tt> (only present if <tt>type</tt> is <tt>"Prio3Histogram"</tt> or <tt>"Prio3SumVec"</tt>)</td>
                <td align="left">This parameter is required by the parallel sum circuit optimization used in these VDAFs. It is a positive number encoded in base 10 as a string.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>bits</tt> (only present if <tt>type</tt> is <tt>"Prio3Sum"</tt>, <tt>"Prio3SumVec"</tt>, or <tt>"Poplar1"</tt>)</td>
                <td align="left">In the case of Prio3Sum or Prio3SumVec, the bit width of the integers being summed, encoded in base 10 as a string. In the case of Poplar1, the bit length of the input, encoded in base 10 as a string.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="query">
          <name>Query</name>
          <t>In multiple APIs defined below, the test runner will need to send a query type,
and in one API, it will need to send a query type along with the associated
query parameters.</t>
          <t>Query types are represented in API requests as numbers, following the values of
the <tt>QueryType</tt> enum in <xref target="DAP"/>.</t>
          <t>Queries are represented in API requests as a nested object, with the following
attributes (new keys will be added as new query types are defined).</t>
          <table anchor="query-object">
            <name>Query JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>type</tt></td>
                <td align="left">A number, representing a query type, as described above.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>batch_interval_start</tt> (only present if <tt>type</tt> is 1, for time interval queries)</td>
                <td align="left">The start of the batch interval, represented as a number equal to the number of seconds since the UNIX epoch.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>batch_interval_duration</tt> (only present if <tt>type</tt> is 1, for time interval queries)</td>
                <td align="left">The duration of the batch interval in seconds, as a number.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>subtype</tt> (only present if <tt>type</tt> is 2, for fixed size queries)</td>
                <td align="left">0 or 1, representing one of the values of the <tt>FixedSizeQueryType</tt> enum in <xref target="DAP"/>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>batch_id</tt> (only present if <tt>type</tt> is 2, for fixed size queries, and <tt>subtype</tt> is 0, for "by batch ID" queries)</td>
                <td align="left">A base64url-encoded DAP <tt>BatchID</tt>.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="client">
        <name>Client</name>
        <section anchor="client-ready">
          <name><tt>/internal/test/ready</tt></name>
          <t>The test runner will POST an empty object (i.e. <tt>{}</tt>) to this endpoint to check
if the Client container is ready to serve requests. If it is ready, it <bcp14>MUST</bcp14>
return a status code of 200 OK.</t>
        </section>
        <section anchor="upload">
          <name><tt>/internal/test/upload</tt></name>
          <t>Upon receipt of this command, the Client container will construct a DAP
report with the given configuration and measurement, and submit it. The Client
container will send its response to the test runner once report submission has
either succeeded or permanently failed.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader</tt></td>
                <td align="left">The Leader's endpoint URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>helper</tt></td>
                <td align="left">The Helper's endpoint URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf</tt></td>
                <td align="left">An object, with the layout given in <xref target="vdaf-object"/>. This determines the VDAF to be used when constructing a report.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>measurement</tt></td>
                <td align="left">If the VDAF's <tt>type</tt> is <tt>"Prio3Count"</tt>: <tt>"0"</tt> or <tt>"1"</tt>. If the VDAF's <tt>type</tt> is <tt>"Prio3Sum"</tt>: a string (representing an integer in base 10). If the VDAF's <tt>type</tt> is <tt>"Prio3SumVec"</tt>: an array of strings, each representing an integer in base 10. If the VDAF's <tt>type</tt> is <tt>"Prio3Histogram"</tt>: a string (representing an integer in base 10). If the VDAF's <tt>type</tt> is <tt>"Poplar1"</tt>: an array of Booleans.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>time</tt> (optional)</td>
                <td align="left">If present, this provides a substitute time value that should be used when constructing the report. If not present, the current system time should be used, as per normal. The time is represented as a number, with a value of the number of seconds since the UNIX epoch.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>time_precision</tt></td>
                <td align="left">A number, providing the precision in seconds of report timestamps.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the report was submitted to the Leader successfully, or <tt>"error"</tt> otherwise.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="aggregator-leader-or-helper">
        <name>Aggregator (Leader or Helper)</name>
        <section anchor="aggregator-ready">
          <name><tt>/internal/test/ready</tt></name>
          <t>The test runner will POST an empty object (i.e. <tt>{}</tt>) to this endpoint to check
if the Aggregator container is ready to serve requests. If it is ready, it <bcp14>MUST</bcp14>
return a status code of 200 OK.</t>
        </section>
        <section anchor="endpoint-for-task">
          <name><tt>/internal/test/endpoint_for_task</tt></name>
          <t>Request the base URL for DAP endpoints for a new task. This API will be invoked
immediately before <tt>/internal/test/add_task</tt> (see <xref target="aggregator-add-task"/>), to
determine the endpoint URLs of the Aggregators. If the Aggregator uses a common
set of DAP endpoints for all tasks, it could always return the same value, such
as the relative URL <tt>/</tt>. Alternately, implementations may wish to generate new
endpoints for each task, derive the endpoint based on the <tt>TaskId</tt>, etc.</t>
          <t>The test runner will provide the hostname at which the Aggregator is externally
reachable. If the Aggregator returns a relative URL, the test runner will
combine it with the hostname into an absolute URL, assuming that the port is
8080. Otherwise, the Aggregator can incorporate the hostname into an absolute
URL and return that.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>role</tt></td>
                <td align="left">Either <tt>"leader"</tt> or <tt>"helper"</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>hostname</tt></td>
                <td align="left">This Aggregator's hostname in the interoperation test environment. This may optionally be used in constructing the endpoint URL as an absolute URL.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the endpoint was successfully selected or set up, or <tt>"error"</tt> otherwise.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>endpoint</tt></td>
                <td align="left">A relative or absolute URL, specifying the DAP Aggregator endpoint that should be used for this task. If the test runner receives a relative URL, it will transform it into an absolute URL before performing the next phase of task setup.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="aggregator-add-task">
          <name><tt>/internal/test/add_task</tt></name>
          <t>Register a task with the Aggregator, with the given configuration and secrets.</t>
          <t>At least one of the HPKE keypairs available for this task should use the
mandatory-to-implement algorithms in section 6 of <xref target="DAP"/>, for broad
compatibility.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader</tt></td>
                <td align="left">The Leader's endpoint URL. The test runner will ensure this is an absolute URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>helper</tt></td>
                <td align="left">The Helper's endpoint URL. The test runner will ensure this is an absolute URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf</tt></td>
                <td align="left">An object, with the layout given in <xref target="vdaf-object"/>. This determines the task's VDAF.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader_authentication_token</tt></td>
                <td align="left">The authentication token that is shared with the other Aggregator, as a string. This string <bcp14>MUST</bcp14> be safe for use as an HTTP header value. When the Leader sends HTTP requests to the Helper, it <bcp14>MUST</bcp14> include this value in a header named <tt>DAP-Auth-Token</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collector_authentication_token</tt> (only present if <tt>role</tt> is <tt>"leader"</tt>)</td>
                <td align="left">The authentication token that is shared between the Leader and Collector, as a string. This string <bcp14>MUST</bcp14> be safe for use as an HTTP header value. When the Collector sends HTTP requests to the Leader, it <bcp14>MUST</bcp14> include this value in a header named <tt>DAP-Auth-Token</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>role</tt></td>
                <td align="left">Either <tt>"leader"</tt> or <tt>"helper"</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf_verify_key</tt></td>
                <td align="left">The VDAF verification key shared by the two Aggregators, encoded with base64url.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>max_batch_query_count</tt></td>
                <td align="left">A number, providing the maximum number of batches any report may be included in, and thus the number of aggregate results it may contribute to.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>query_type</tt></td>
                <td align="left">A number, representing the task's query type, as described in <xref target="query"/>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>min_batch_size</tt></td>
                <td align="left">A number, providing the minimum number of reports that must be in a batch for it to be collected.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>max_batch_size</tt> (only present if <tt>query_type</tt> is 2, for fixed size queries)</td>
                <td align="left">A number, providing the maximum number of reports that may be in a batch for it to be collected, or null, if there is no maximum.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>time_precision</tt></td>
                <td align="left">A number, providing the precision in seconds of report timestamps. For tasks using the time interval query type, the batch interval's duration will always be a multiple of this value.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collector_hpke_config</tt></td>
                <td align="left">The Collector's HPKE configuration, encoded in base64url, for encryption of aggregate shares.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>task_expiration</tt></td>
                <td align="left">A number, providing the time when Clients are no longer expected to upload to this task. This is represented as a number of seconds since the UNIX epoch.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the task was successfully set up, or <tt>"error"</tt> otherwise. (for example, if the VDAF was not supported)</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="collector">
        <name>Collector</name>
        <section anchor="collector-ready">
          <name><tt>/internal/test/ready</tt></name>
          <t>The test runner will POST an empty object (i.e. <tt>{}</tt>) to this endpoint to check
if the Collector container is ready to serve requests. If it is ready, it <bcp14>MUST</bcp14>
return a status code of 200 OK.</t>
        </section>
        <section anchor="collector-add-task">
          <name><tt>/internal/test/add_task</tt></name>
          <t>Register a task with the Collector, with the given configuration. Returns the
Collector's HPKE configuration for this task.</t>
          <t>The HPKE keypair generated for this task should use the mandatory-to-implement
algorithms in section 6 of <xref target="DAP"/>, for broad compatibility.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader</tt></td>
                <td align="left">The Leader's endpoint URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf</tt></td>
                <td align="left">An object, with the layout given in <xref target="vdaf-object"/>. This determines the task's VDAF.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collector_authentication_token</tt></td>
                <td align="left">The authentication token that is shared between the Leader and Collector, as a string. This string <bcp14>MUST</bcp14> be safe for use as an HTTP header value. When the Collector sends HTTP requests to the Leader, it <bcp14>MUST</bcp14> include this value in a header named <tt>DAP-Auth-Token</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>query_type</tt></td>
                <td align="left">A number, representing the task's query type, as described in <xref target="query"/>.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the task was successfully set up, or <tt>"error"</tt> otherwise. (for example, if the VDAF was not supported)</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collector_hpke_config</tt> (if successful)</td>
                <td align="left">The Collector's HPKE configuration, encoded in base64url, for encryption of aggregate shares.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="collection-start">
          <name><tt>/internal/test/collection_start</tt></name>
          <t>Send a collection request to the Leader with the provided parameters, and return
a handle to the test runner identifying this collection job. The test runner
will provide this handle to the Collector in subsequent
<tt>/internal/test/collection_poll</tt> requests (see <xref target="collection-poll"/>).</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>agg_param</tt></td>
                <td align="left">A base64url-encoded aggregation parameter.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>query</tt></td>
                <td align="left">An object, with the layout given in <xref target="query-object"/>. This provides the collection job's query, and in turn determines which reports should be included.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the collection request succeeded, or <tt>"error"</tt> otherwise.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>handle</tt> (if successful)</td>
                <td align="left">A handle produced by the Collector to refer to this collection job. This must be a string.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="collection-poll">
          <name><tt>/internal/test/collection_poll</tt></name>
          <t>The test runner sends this command to a Collector to poll for completion of the
collection job associated with the provided handle. The Collector provides the
status and (if available) results to the test runner.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>handle</tt></td>
                <td align="left">The handle for a collection job from a previous invocation of <tt>/internal/test/collection_start</tt>. (see <xref target="collection-start"/>)</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">Either <tt>"complete"</tt> if the result is ready, <tt>"in progress"</tt> if the result is not yet ready, or <tt>"error"</tt> if an error occurred.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>batch_id</tt> (if the task uses fixed size queries)</td>
                <td align="left">The identifier of the batch that was collected, encoded with base64url.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>report_count</tt> (if complete)</td>
                <td align="left">A number, reflecting the count of Client reports included in this aggregated result.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>interval_start</tt> (if complete)</td>
                <td align="left">The start of the collection's interval, represented as a number equal to the number of seconds since the UNIX epoch.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>interval_duration</tt> (if complete)</td>
                <td align="left">The duration of the collection's interval in seconds, as a number.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>result</tt> (if complete)</td>
                <td align="left">The result of the aggregation. If the VDAF is of type Prio3Count or Prio3Sum, this will be a string, representing an integer in base 10. If the VDAF is of type Prio3Histogram, Prio3SumVec, or Poplar1, this will be an array of strings, each representing an integer in base 10.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="test-cases">
        <name>Test Cases</name>
        <t>Test cases could be written to cover the following scenarios.</t>
        <ul spacing="normal">
          <li>
            <t>Test successful aggregations with each VDAF.</t>
          </li>
          <li>
            <t>Test an aggregation over a few hundred or thousand reports, to exercise the
Aggregators' division of reports into aggregation jobs.</t>
          </li>
          <li>
            <t>Test that uploading a report with a time far in the future is rejected.</t>
          </li>
          <li>
            <t>Confirm that Leaders and Helpers reject requests with respective
authentication tokens that are incorrect.</t>
          </li>
          <li>
            <t>Test enforcement of <tt>max_batch_query_count</tt> by making overlapping collection
requests.</t>
          </li>
          <li>
            <t>Perform an entire aggregation and collection flow, attempt to upload a late
report that falls into the same batch interval, and test that performing the
collection request a second time yields the same result.</t>
          </li>
          <li>
            <t>Attempt to upload a canned report from the test runner more than once, and
confirm that anti-replay measures were effective by inspecting the aggregation
result.</t>
          </li>
        </ul>
      </section>
      <section anchor="other-test-considerations">
        <name>Other Test Considerations</name>
        <t>All test cases should automatically fail after a generous timeout.</t>
        <t>It is the responsibility of the test runner to wait for all containers to start
up and respond successfully to a request to <tt>/internal/test/ready</tt> before
sending any further commands.</t>
        <t>Aggregator URLs will be constructed by the test runner with hostnames that
resolve to the respective containers within the container network.</t>
        <t>A reverse proxy could be introduced in front of each Aggregator to inject
failures when sending requests or responses, to test round skew recovery
stragegies and overall implementation resilience.</t>
      </section>
      <section anchor="test-runner-operation">
        <name>Test Runner Operation</name>
        <t>The following sequence outlines how the test runner will use the above APIs on
port 8080 of each container to perform a typical integration test, executing a
successful aggregation.</t>
        <ol spacing="normal" type="1"><li>
            <t>Create and start containers.</t>
          </li>
          <li>
            <t>Set up networking between containers.</t>
          </li>
          <li>
            <t>Try sending <tt>/internal/test/ready</tt> requests to each container, and retry
until they succeed.</t>
          </li>
          <li>
            <t>Generate a random <tt>TaskId</tt>, random authentication tokens, and a VDAF
verification key.</t>
          </li>
          <li>
            <t>Send a <tt>/internal/test/endpoint_for_task</tt> request (<xref target="endpoint-for-task"/>) to
the Leader.</t>
          </li>
          <li>
            <t>Send a <tt>/internal/test/endpoint_for_task</tt> request (<xref target="endpoint-for-task"/>) to
the Helper.</t>
          </li>
          <li>
            <t>Construct Aggregator URLs using the above responses.</t>
          </li>
          <li>
            <t>Send a <tt>/internal/test/add_task</tt> request (<xref target="collector-add-task"/>) to the
Collector. (the Collector generates an HPKE key pair as a side-effect)</t>
          </li>
          <li>
            <t>Send a <tt>/internal/test/add_task</tt> request (<xref target="aggregator-add-task"/>) to the
Leader.</t>
          </li>
          <li>
            <t>Send a <tt>/internal/test/add_task</tt> request (<xref target="aggregator-add-task"/>) to the
Helper.</t>
          </li>
          <li>
            <t>Send one or more <tt>/internal/test/upload</tt> requests (<xref target="upload"/>) to the Client.</t>
          </li>
          <li>
            <t>Send one or more <tt>/internal/test/collection_start</tt> requests
(<xref target="collection-start"/>) to the Collector. (this provides a handle for use in
the next step)</t>
          </li>
          <li>
            <t>Send <tt>/internal/test/collection_poll</tt> requests (<xref target="collection-poll"/>) to the
Collector, polling until each collection is completed. (the Collector will
provide the calculated aggregate results)</t>
          </li>
          <li>
            <t>Stop containers.</t>
          </li>
          <li>
            <t>Copy logs out of each container.</t>
          </li>
          <li>
            <t>Delete containers, and clean up container networking resources.</t>
          </li>
        </ol>
      </section>
    </section>
    <section anchor="implementation-status">
      <name>Implementation Status</name>
      <t><xref target="Janus"/>, <xref target="divviup-ts"/>, and <xref target="Daphne"/> currently implement a version of this test
interface. <xref target="REF-IMPL"/> is a reference implementation of a test runner using this
interface.</t>
      <t>Additional DAP implementations would be warmly welcomed.</t>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>Any DAP implementation that adopts this testing interface should ensure that the
test-only APIs described herein are only present in software used for testing
purposes, and not in production systems.</t>
    </section>
    <section anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>References</name>
      <references anchor="sec-normative-references">
        <name>Normative References</name>
        <reference anchor="DAP">
          <front>
            <title>Distributed Aggregation Protocol for Privacy Preserving Measurement</title>
            <author fullname="Tim Geoghegan" initials="T." surname="Geoghegan">
              <organization>ISRG</organization>
            </author>
            <author fullname="Christopher Patton" initials="C." surname="Patton">
              <organization>Cloudflare</organization>
            </author>
            <author fullname="Brandon Pitman" initials="B." surname="Pitman">
              <organization>ISRG</organization>
            </author>
            <author fullname="Eric Rescorla" initials="E." surname="Rescorla">
              <organization>Mozilla</organization>
            </author>
            <author fullname="Christopher A. Wood" initials="C. A." surname="Wood">
              <organization>Cloudflare</organization>
            </author>
            <date day="18" month="December" year="2023"/>
            <abstract>
              <t>   There are many situations in which it is desirable to take
   measurements of data which people consider sensitive.  In these
   cases, the entity taking the measurement is usually not interested in
   people's individual responses but rather in aggregated data.
   Conventional methods require collecting individual responses and then
   aggregating them, thus representing a threat to user privacy and
   rendering many such measurements difficult and impractical.  This
   document describes a multi-party distributed aggregation protocol
   (DAP) for privacy preserving measurement (PPM) which can be used to
   collect aggregate data without revealing any individual user's data.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-ietf-ppm-dap-09"/>
        </reference>
        <reference anchor="VDAF">
          <front>
            <title>Verifiable Distributed Aggregation Functions</title>
            <author fullname="Richard Barnes" initials="R." surname="Barnes">
              <organization>Cisco</organization>
            </author>
            <author fullname="David Cook" initials="D." surname="Cook">
              <organization>ISRG</organization>
            </author>
            <author fullname="Christopher Patton" initials="C." surname="Patton">
              <organization>Cloudflare</organization>
            </author>
            <author fullname="Phillipp Schoppmann" initials="P." surname="Schoppmann">
              <organization>Google</organization>
            </author>
            <date day="20" month="November" year="2023"/>
            <abstract>
              <t>   This document describes Verifiable Distributed Aggregation Functions
   (VDAFs), a family of multi-party protocols for computing aggregate
   statistics over user measurements.  These protocols are designed to
   ensure that, as long as at least one aggregation server executes the
   protocol honestly, individual measurements are never seen by any
   server in the clear.  At the same time, VDAFs allow the servers to
   detect if a malicious or misconfigured client submitted an
   measurement that would result in an invalid aggregate result.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-vdaf-08"/>
        </reference>
        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
        <reference anchor="RFC8729">
          <front>
            <title>The RFC Series and RFC Editor</title>
            <author fullname="R. Housley" initials="R." role="editor" surname="Housley"/>
            <author fullname="L. Daigle" initials="L." role="editor" surname="Daigle"/>
            <date month="February" year="2020"/>
            <abstract>
              <t>This document describes the framework for an RFC Series and an RFC Editor function that incorporate the principles of organized community involvement and accountability that has become necessary as the Internet technical community has grown, thereby enabling the RFC Series to continue to fulfill its mandate. This document obsoletes RFC 4844.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="8729"/>
          <seriesInfo name="DOI" value="10.17487/RFC8729"/>
        </reference>
        <reference anchor="RFC4648">
          <front>
            <title>The Base16, Base32, and Base64 Data Encodings</title>
            <author fullname="S. Josefsson" initials="S." surname="Josefsson"/>
            <date month="October" year="2006"/>
            <abstract>
              <t>This document describes the commonly used base 64, base 32, and base 16 encoding schemes. It also discusses the use of line-feeds in encoded data, use of padding in encoded data, use of non-alphabet characters in encoded data, use of different encoding alphabets, and canonical encodings. [STANDARDS-TRACK]</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="4648"/>
          <seriesInfo name="DOI" value="10.17487/RFC4648"/>
        </reference>
      </references>
      <references anchor="sec-informative-references">
        <name>Informative References</name>
        <reference anchor="SI2020" target="https://research.protocol.ai/publications/automating-quic-interoperability-testing/seemann2020.pdf">
          <front>
            <title>Automating QUIC Interoperability Testing</title>
            <author initials="M." surname="Seemann">
              <organization/>
            </author>
            <author initials="J." surname="Iyengar">
              <organization/>
            </author>
            <date year="2020" month="August" day="10"/>
          </front>
        </reference>
        <reference anchor="Janus" target="https://github.com/divviup/janus">
          <front>
            <title>Experimental implementation of the DAP specification</title>
            <author>
              <organization/>
            </author>
            <date year="2022" month="August" day="25"/>
          </front>
        </reference>
        <reference anchor="divviup-ts" target="https://github.com/divviup/divviup-ts">
          <front>
            <title>TypeScript client for https://divviup.org</title>
            <author>
              <organization/>
            </author>
            <date year="2022" month="October" day="05"/>
          </front>
        </reference>
        <reference anchor="REF-IMPL" target="https://github.com/divergentdave/dap-interop-test-runner">
          <front>
            <title>Reference DAP interoperation test harness</title>
            <author>
              <organization/>
            </author>
            <date year="2022" month="October" day="04"/>
          </front>
        </reference>
        <reference anchor="Daphne" target="https://github.com/cloudflare/daphne">
          <front>
            <title>Implementation of DAP</title>
            <author>
              <organization/>
            </author>
            <date year="2022" month="December" day="15"/>
          </front>
        </reference>
      </references>
    </references>
    <?line 469?>

<section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>Thanks to Brandon Pitman, Christopher Patton, and Tim Geoghegan for feedback and
contributions.</t>
    </section>
  </back>
  <!-- ##markdown-source:
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
AA==

-->

</rfc>
