<?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.6.25 (Ruby 3.1.3) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-dcook-ppm-dap-interop-test-design-03" category="info" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.16.0 -->
  <front>
    <title>DAP Interoperation Test Design</title>
    <seriesInfo name="Internet-Draft" value="draft-dcook-ppm-dap-interop-test-design-03"/>
    <author fullname="David Cook">
      <organization>ISRG</organization>
      <address>
        <email>dcook@divviup.org</email>
      </address>
    </author>
    <date year="2023" month="March" day="13"/>
    <area>Security</area>
    <workgroup>Privacy Preserving Measurement</workgroup>
    <abstract>
      <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>
    <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
aggregator, helper aggregator, 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>
    </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>"Prio3Aes128Count"</tt>, <tt>"Prio3Aes128CountVec"</tt>, <tt>"Prio3Aes128Sum"</tt>, <tt>"Prio3Aes128Histogram"</tt>, or <tt>"Poplar1Aes128"</tt></td>
              </tr>
              <tr>
                <td align="left">
                  <tt>length</tt> (only present if <tt>type</tt> is <tt>"Prio3Aes128CountVec"</tt>)</td>
                <td align="left">The length of the vectors being summed, (encoded in base 10 as a string) used to parameterize the Prio3Aes128CountVec VDAF.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>bits</tt> (only present if <tt>type</tt> is <tt>"Prio3Aes128Sum"</tt> or <tt>"Poplar1Aes128"</tt>)</td>
                <td align="left">In the case of Prio3Aes128Sum, the bit width of the integers being summed, encoded in base 10 as a string. In the case of Poplar1Aes128, the bit length of the input, encoded in base 10 as a string.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>buckets</tt> (only present if <tt>type</tt> is <tt>"Prio3Aes128Histogram"</tt>)</td>
                <td align="left">An array of histogram bucket boundaries, (encoded in base 10 as strings) used to parameterize the Prio3Aes128Histogram VDAF.</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>"Prio3Aes128Count"</tt>: <tt>"0"</tt> or <tt>"1"</tt>. If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128CountVec"</tt>: an array of strings, each of which is <tt>"0"</tt> or <tt>"1"</tt>. If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128Sum"</tt>: a string (representing an integer in base 10). If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128Histogram"</tt>: a string (representing an integer in base 10). If the VDAF's <tt>type</tt> is <tt>"Poplar1Aes128"</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>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. 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 times any report can be included in a collect request.</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.</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="collect-start">
          <name><tt>/internal/test/collect_start</tt></name>
          <t>Send a collect request to the leader with the provided parameters, and return a
handle to the test runner identifying this collect request. The test runner will
provide this handle to the collector in subsequent <tt>/internal/test/collect_poll</tt>
requests (see <xref target="collect-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 collect request'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 collect 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 collect request. This must be a string.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="collect-poll">
          <name><tt>/internal/test/collect_poll</tt></name>
          <t>The test runner sends this command to a collector to poll for completion of the
collect job associated with the provided handle. The collector provides the
status and (if available) result 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 collect request from a previous invocation of <tt>/internal/test/collect_start</tt>. (see <xref target="collect-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>result</tt> (if complete)</td>
                <td align="left">The result of the aggregation. If the VDAF is of type Prio3Aes128Count or Prio3Aes128Sum, this will be a string, representing an integer in base 10. If the VDAF is of type Prio3Aes128CountVec, Prio3Aes128Histogram, or Poplar1Aes128, 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>Test successful aggregations with each VDAF.</li>
          <li>Test an aggregation over a few hundred or thousand reports, to exercise the
aggregators' division of reports into aggregation jobs.</li>
          <li>Test that uploading a report with a time far in the future is rejected.</li>
          <li>Confirm that leaders and helpers reject requests with respective
authentication tokens that are incorrect.</li>
          <li>Test enforcement of <tt>max_batch_query_count</tt> by making overlapping collect
requests.</li>
          <li>Perform an entire aggregation and collect flow, attempt to upload a late
report that falls into the same collect interval, and test that performing the
collect request a second time yields the same result.</li>
          <li>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.</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>Once a future <xref target="DAP"/> draft solves the issue of retries in the aggregate flow, a
reverse proxy could be introduced in front of each aggregator to inject failures
when sending requests or responses, to test the protocol's resilience. (It is
known such a test would fail with the current protocol.)</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>Create and start containers.</li>
          <li>Set up networking between containers.</li>
          <li>Try sending <tt>/internal/test/ready</tt> requests to each container, and retry
until they succeed.</li>
          <li>Generate a random <tt>TaskId</tt>, random authentication tokens, and a VDAF
verification key.</li>
          <li>Send a <tt>/internal/test/endpoint_for_task</tt> request (<xref target="endpoint-for-task"/>) to
the leader.</li>
          <li>Send a <tt>/internal/test/endpoint_for_task</tt> request (<xref target="endpoint-for-task"/>) to
the helper.</li>
          <li>Construct aggregator URLs using the above responses.</li>
          <li>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)</li>
          <li>Send a <tt>/internal/test/add_task</tt> request (<xref target="aggregator-add-task"/>) to the
leader.</li>
          <li>Send a <tt>/internal/test/add_task</tt> request (<xref target="aggregator-add-task"/>) to the
helper.</li>
          <li>Send one or more <tt>/internal/test/upload</tt> requests (<xref target="upload"/>) to the client.</li>
          <li>Send one or more <tt>/internal/test/collect_start</tt> requests (<xref target="collect-start"/>)
to the collector. (this provides a handle for use in the next step)</li>
          <li>Send <tt>/internal/test/collect_poll</tt> requests (<xref target="collect-poll"/>) to the
collector, polling until each collection is completed. (the collector will
provide the calculated aggregate results)</li>
          <li>Stop containers.</li>
          <li>Copy logs out of each container.</li>
          <li>Delete containers, and clean up container networking resources.</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>
        <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="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="9" month="December" year="2022"/>
            <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-03"/>
        </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="Christopher Patton" initials="C." surname="Patton">
              <organization>Cloudflare</organization>
            </author>
            <author fullname="Phillipp Schoppmann" initials="P." surname="Schoppmann">
              <organization>Google</organization>
            </author>
            <date day="24" month="August" year="2022"/>
            <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 input that
   would result in an incorrect aggregate result.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-vdaf-03"/>
        </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">
              <organization/>
            </author>
            <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">
              <organization/>
            </author>
            <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">
              <organization/>
            </author>
            <author fullname="L. Daigle" initials="L." role="editor" surname="Daigle">
              <organization/>
            </author>
            <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">
              <organization/>
            </author>
            <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>
        <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>
    <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:
H4sIAAAAAAAAA+1c63LcxpX+j6foHf+Q6JrhRdEmDmuzFZqUbMa60CLtbEql
0vQAPTOwMACMBkhNJFblNbZqt2qfZR8lT7LfOacbaMyAFG1Lm2xtUqmyiAG6
T5/Ldy59uieTSVSndWYO1ejk6Eyd5rWpitJUuk6LXF0YW6sTY9NFPopiXZtF
Ua0PVZrPiyhKijjXK3yZVHpeT5K4KN5MynI1SXQ5SWWgSY0RJgmPMNn/VWSb
2Sq1FmPX6xKfnj66eBylZXWo6qqx9YP9/d/uP4jyZjUz1WGUYMbDKC5ya3Lb
2EM115k10eWh+lWkK6NB9LmJmyqt16PoqqjeLKqiKfH0rEovdbxWZ5WxprpM
84V6arRtKrMyeT2KLk3eYGSl7vqBUkLv6I+YhX79ij6k5yudZniOdf8+NfV8
t6gW9FhX8RKPl3Vd2sO9PXqLHqWXZte/tkcP9mZVcWXNHr7fo+8Wab1sZvgy
wavVApMn+tLs3ZHFNEKm6UEwd2+kXZlgNy3uOuZd39td1qtsFEW6qZdFRcyd
qHmTZaIjJ/oyTdQxxsAPSmH5Ok//zFoGLTh/8RU/No6bPNnvQfhl2pTC0igv
qhXev2S5QVfx2eRkV4gjlra0Qc2U+v7k6HHvjQpvxPNqMblM9JzeiUiLgyHP
Tx/sP9g/ZDq8SRw1dUFvQODffnd6HJjHLM2gdWwg+HXEX7G6Khplsv/F5GBf
htLgfSgO0jCS/G5ZFXURF9muTvfKZpalMbPD7ul21smPTRp7ZrezMtfx6541
YFie04y7ZTIXKjr+0/8mMFYYztNddS4v95//YVedrk2+0BWe/0HnMLKQAY/e
YtaUTEBnKl2VGZuDYEMxV/XSkCSULU2czh39fVY8IFY8+OceKzwnnCrGxWrP
iXrvByIBL7u/J3WfntEFrPA8rtKyVnGWghYFGapA0zuF2SDjAEK5Mxnd9Pji
xaPHk9OnZ0/6lLwwc1OZPBYOpH3cJAGppa5yY+0gJQ/vQElo/psmVzV5bkhm
J7pc5qZH2umWnEDhFhH4/wfZEWdFk8wBXEwB5omiaDKZKD2zdaXjOooulqlV
cAQNzacSM0+xZKUVvl55PjDhcw1Okaz6WmSdGkUnKYZMZ01tEnW0WFRmIdSf
OSPhb29HaXUf69xROk8iIBKUZAZSlsUVxgeRG6TEOlczoxqL+epCQXIEBkNy
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
5XJqWEj6H7Iytrn4XAAA

-->

</rfc>
