<?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-05" category="info" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.17.5 -->
  <front>
    <title>DAP Interoperation Test Design</title>
    <seriesInfo name="Internet-Draft" value="draft-dcook-ppm-dap-interop-test-design-05"/>
    <author fullname="David Cook">
      <organization>ISRG</organization>
      <address>
        <email>dcook@divviup.org</email>
      </address>
    </author>
    <date year="2023" month="August" day="15"/>
    <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>"Prio3CountVec"</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>"Prio3CountVec"</tt>, <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>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>"Prio3CountVec"</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>"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 Prio3CountVec, 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="10" month="July" 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-05"/>
        </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="15" month="June" 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 input that
   would result in an incorrect aggregate result.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-vdaf-06"/>
        </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 468?>

<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>
