<?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.35 (Ruby 3.2.2) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-dcook-ppm-dap-interop-test-design-04" category="info" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.17.3 -->
  <front>
    <title>DAP Interoperation Test Design</title>
    <seriesInfo name="Internet-Draft" value="draft-dcook-ppm-dap-interop-test-design-04"/>
    <author fullname="David Cook">
      <organization>ISRG</organization>
      <address>
        <email>dcook@divviup.org</email>
      </address>
    </author>
    <date year="2023" month="June" day="14"/>
    <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>"Prio3Sum"</tt>, <tt>"Prio3SumVec"</tt>, <tt>"Prio3Histogram"</tt>, or <tt>"Poplar1"</tt></td>
              </tr>
              <tr>
                <td align="left">
                  <tt>length</tt> (only present if <tt>type</tt> is <tt>"Prio3CountVec"</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>
              <tr>
                <td align="left">
                  <tt>buckets</tt> (only present if <tt>type</tt> is <tt>"Prio3Histogram"</tt>)</td>
                <td align="left">An array of histogram bucket boundaries, encoded in base 10 as strings.</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="13" month="March" 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-04"/>
        </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="13" month="March" 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-05"/>
        </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:
H4sIAAAAAAAAA+1c63LcxrH+j6eYs/5hMbW7JBXFcVi50aRkM9aFFunkpFQq
LhaY3YWJBWAMQGoj8V3Os5wnO193zwADLHiRbSU5VUmlyiIWmOnpy9eX6ZnJ
ZBJUSZXqAzU6PjxVJ1mly7zQZVgleabOtanUsTbJMhsFUVjpZV5uDlSSLfIg
iPMoC9f4Mi7DRTWJozy/nBTFehKHxSSRgSYVRpjEPMJk70lg6vk6MQZjV5sC
n548PX8WJEV5oKqyNtXjvb3f7T0Osno91+VBEGPGgyDKM6MzU5sDtQhTo4Or
A/XrICx1CKLPdFSXSbUZBdd5ebks87rA09MyuQqjjTottdHlVZIt1QsdmrrU
a51Vo+BKZzVGVuqhHygl9I7+hlno16/pQ3q+DpMUz7HuPye6WkzzckmPwzJa
4fGqqgpzsLtLb9Gj5EpP3Wu79GB3XubXRu/i+136bplUq3qOL2O8Wi4xeRxe
6d0HsphGSEN64M3dGWkqE0yT/KFjPvS96apap6MgCOtqlZfE3Ila1GkqOnIc
XiWxOsIY+EEpLD/Mkn+wlkELzl5/zY+15SZP9mcQfpXUhbA0yPJyjfevWG7Q
VXw2OZ4KccTShjaomVJ/PT581nmjxBvRolxOruJwMdn7TRCQFntDnp083nu8
d8B0OJM4rKuc3oDAv/v+5Mgzj3mSQuvYQPDriL9idVU0ymTvy8n+ngwVgve+
OEjDSPLTosyrPMrTaZjsFvU8TSJmh9kNm1knP9ZJ5JjdzMpcx6+7RoNhWUYz
Tot4IVS0/Kf/TWCsMJwXU3UmL3ef/2WqTjY6W4Ylnv8lzGBkPgOevsOsCZlA
mKpkXaRsDoIN+UJVK02SUKbQUbKw9HdZ8ZhY8fg3HVY4TlhVjPL1rhX17g9E
Al62f0+qLj2jc1jhWVQmRaWiNAEtCjJUnqa3CtMjY3+PhP5AMtrp8cXrp88m
Jy9On3cpea0XutRZJBxIurhJAlKrsMy0MYOUPHkAJb75902urLNMk8yOw2KV
6Q5pJ1tyAoVbROD/97IjSvM6XgC4mALMEwTBZDJR4dxUZRhVQXC+SoyCI6hp
PhXrRYIlq1Dh67XjAxO+CMEpklVXi4xVo+A4wZDJvK50rA6Xy1IvhfpTayT8
7d0orR5hnTsqzOIAiAQlmYOUVX6N8UFkj5QozNRcq9pgvipXkByBwZAcMUsw
19W11hkrfG8BU8YArLnUQLVopYk1tIpr8BF8wHvgSWnGRJjK9HXAAsyzdKMO
T0/kQ0xb5nEdWWLK/CohH8mqVYXm0vDHSZZUCcamFyJoFiibWomskzhOIZ7P
CKJ4KDbFfy/5vMFy3oJfoCnozxxGBG3kuoZkYJSTQJws2O4qVYRllURJwUAZ
sBH2JXNomIOr0IC8usTHJM6oUg56VZmnGqJ5dMRgMlbPdRjrchx8o1MQIDI7
ytNUR1Ve7sDZZzUh4aCWNDSGaUoMnidZw8NAX9EPyqzpR4lvrGVucTzK6zQm
5azCdyRiMEyrZY55WRZgnhPo5+YWQZK+50pn4TzVyrmTBrON7q2ApQE2mBw/
h9VtTN6mVIxIv4tWYbaEZpAS6DBaBTmmsewjmzGIAYRGUydVY32lJiBTuS/W
PuO2hHpGT5LFhgkaFsR1AiaHCVmMSmJ8K68Huizz0vDT7qhj/zUVgoIl6ExY
GXkBTmGCjquTBZYa5kbfwShKzcHt0Bww1fPwUqg2RWJJXpT5Wn0HL29Di9cM
7OqNRCNvx8zOnq73HDHDBRAPfCQIUusciNIAD8gIl2TzMSRCs0MxnP0HjcbA
VkQ+4liEgwauoeJR29GchFsTamiD/VCsvgDvQEAGY7Cxso+efUBEZB+rq7BM
8tqob87PTwkUwckfa1LJaQATLpgKUX1f3xsFoAWMeXQegKBHl43o2hkDWVVd
FDlGNPmaqLxWLR67+UHb9SrBIkWR0pSdiO5wqMqDFqcNfD1hN6CayIW2V0SB
LDAqNRkXQOha03CwzTJZLmkMMg1h8iJoTQBJ0sSxl9QGqH6UZ1ekoKRaNOox
IXnCfxPIa3WpNwr8jo0avfj+7Hw0lv+ql6/436+fIn59/fSY/n32zeHz580/
AvvG2Tevvn9+3P6r/fLo1YsXT18ey8d4qjqPgtGLw7+PRJijV6fnJ69eHj4f
CfN930NuDqo3t9hTgCngWGgaX83W+tXR6f/+z/4T9f79f71+dvR4f/93Nzf2
jy/3f/sEf1yvtLU7Fpn8CUZugrAoEFjTKAzAYQF/kgrjDcKATMEANNj5qzfE
mbcH6vfzqNh/8kf7gBbceeh41nnIPNt+svWxMHHg0cA0DTc7z3uc7tJ7+PfO
347v3sPf/ymF0qvJ/pd/+mPgdMga8YmzmiB4ugUvAz4Jfm/jYOZOlBkH9Os2
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
kIxyK8rn9LqoNm5yyWlm729mO6KOiWnTKzyIVjq6DGxALKT4tTWjeMq2sNHU
5SnWTKrmDUZHSvuCu2PvaTC80rpI8zCmpcq/sMjvEdkqjsuLbvUf8hsP08t8
oG4hZjlokKIGJ7INhEm1rZPMsUp0Uicu3EtRMLFVPSuorSoYeYnKtKG4tXpf
PjmZu6WjbYCibclAJ5w6dPIDJPNYJCaDcttcYQsqkaWSCdyuned44ySeTSUo
o4rYjMFAqmOfe2rw/evn/NaKK2jyllTTht4iRzwj97/lG9JwQ+UR4S/X/3yn
fWMTxlhLoYqKRDaSttsCXEKjcn4rQgF+4RzP7glp9uFk0QwBSocDzNHsAH/v
uSAT8Rur7r3fceh3QPbUhDk2ZrE7cpz3UVmQP/yoCTjOPGjiMPWo6+gyFyN6
MdPOQwa9l+b757l3Gi8I/CVX4ILrLvVf5TlUNzMsenJwBPkFWWyY7pD47aRj
AYdmHzTk2jIQuaZqKDlGdjRSY7A1gtsVjmsUonJEMuXC3jyIwetSduk2iJHW
Mn53UPajVI3mlrXU7gqwgza3xQzWkEJLqnWJHxE70PgXGDriHUk/jhK+uKU1
r3iOnyaw+ETDALrXRTc4tzW7WzzbTR+fBPxnH2YjW9aAfVgv4/CYsgBG2Kot
btrSfbfYwnbFO/dkZASX14mReI+fdpSCgMn+ofhX4DoS+aXmggQi8oT7JFRV
5jUVXld5XtkeC6/aO1hwsWBOJeMeayzy38ob8vPeltIju0z8U3B25x73Hzbf
fuoQwKPyXxEGOLouEI9dkI+j1buHEzyc0EMs36mjhLPgPdwTB3Hk+9ptHXoS
SqUJ31khU7bTCvoqv0R+lqy5WkxbLHi8oK3NPm3IaSxJj4zW8G6eVPCbUHaz
Q3oWNE6us81ERDbBbstp0yCjx/3aeG1atJPimoV6i6ONH+oPY85L41CYXocb
44qgzXYV48qYbGsVhMaaot2BIu7NdrkozkuWraZ+DZZ3TBOzIj1Y6oy2HLiB
IujSxK6GiBrD2ZdJb6+NxRW7WrALU+Cgqmh6i1q7jWe368KlPCC5+N8e40i7
34nc0g3UEMTQDssQj4VDhiOMlhHDhYJA2pK0VAFsxNMQw3tL5LnmJk/J6fA4
QJt6LbAbiqoy8iUmkC3WVw7Lxlu2xy40ykt84Hb0bp0sIOlJH5KVeFj9zICR
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
cgW7kM3fBXwtTcrRe9ONJCT9H/2dnBPAXQAA

-->

</rfc>
