<?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.39 (Ruby 3.2.2) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-dcook-ppm-dap-interop-test-design-06" category="info" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.18.0 -->
  <front>
    <title>DAP Interoperation Test Design</title>
    <seriesInfo name="Internet-Draft" value="draft-dcook-ppm-dap-interop-test-design-06"/>
    <author fullname="David Cook">
      <organization>ISRG</organization>
      <address>
        <email>dcook@divviup.org</email>
      </address>
    </author>
    <date year="2023" month="September" day="19"/>
    <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.</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>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 collection
requests.</li>
          <li>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.</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>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>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/collection_start</tt> requests
(<xref target="collection-start"/>) to the Collector. (this provides a handle for use in
the next step)</li>
          <li>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)</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="14" month="September" 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-07"/>
        </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="31" month="August" 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-07"/>
        </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>
        <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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

-->

</rfc>
