<?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.17 (Ruby 3.1.2) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-dcook-ppm-dap-interop-test-design-02" category="info" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.15.2 -->
  <front>
    <title>DAP Interoperation Test Design</title>
    <seriesInfo name="Internet-Draft" value="draft-dcook-ppm-dap-interop-test-design-02"/>
    <author fullname="David Cook">
      <organization>ISRG</organization>
      <address>
        <email>dcook@divviup.org</email>
      </address>
    </author>
    <date year="2022" month="November" day="06"/>
    <area>Security</area>
    <workgroup>Privacy Preserving Measurement</workgroup>
    <abstract>
      <t>This document defines a common test interface for implementations of the
Distributed Aggregation Protocol for Privacy Preserving Measurement (DAP) and
describes how this test interface can be used to perform interoperation testing
between the implementations. Tests are orchestrated with containers, and new
test-only APIs are introduced to provision DAP tasks and initiate processing.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://divergentdave.github.io/draft-dcook-ppm-dap-interop-test-design/draft-dcook-ppm-dap-interop-test-design.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-dcook-ppm-dap-interop-test-design/"/>.
      </t>
      <t>
        Discussion of this document takes place on the
        Privacy Preserving Measurement Working Group mailing list (<eref target="mailto:ppm@ietf.org"/>),
        which is archived at <eref target="https://mailarchive.ietf.org/arch/browse/ppm/"/>.
        Subscribe at <eref target="https://www.ietf.org/mailman/listinfo/ppm/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/divergentdave/draft-dcook-ppm-dap-interop-test-design"/>.</t>
    </note>
  </front>
  <middle>
    <section anchor="introduction">
      <name>Introduction</name>
      <t>This document defines a common test interface for implementations of the
Distributed Aggregation Protocol for Privacy Preserving Measurement <xref target="DAP"/>. This
test interface facilitates interoperation tests between different participating
DAP implementations. As DAP has four distinct protocol roles, (client, leader
aggregator, helper aggregator, and collector) manual interoperation testing
between all combinations of even a small number of DAP implementations could be
taxing. The goal of this document's common test interface is to enable
automation of these interoperation tests, so that different participating
implementations can be exchanged for each other, and the same test suite can be
re-run on different combinations of implementations. Simplifying interoperation
testing will aid in identifying errors in implementations, identifying
ambiguities in the protocol specification, and reducing regressions in
implementations.</t>
      <t>Taking inspiration from QuicInteropRunner <xref target="SI2020"/>, each participating
implementation provides one or more container images adhering to a common
interface. A test runner will start one container for each protocol participant,
configure networking between the containers, and send various HTTP API requests.
As part of this common testing interface, the HTTP servers in the containers
will support some new test-only HTTP APIs, which will allow the test runner to
provision shared task parameters and secrets, as well as trigger the start of
different sub-protocols.</t>
    </section>
    <section anchor="conventions-and-definitions">
      <name>Conventions and Definitions</name>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
appear in all capitals, as shown here.</t>
    </section>
    <section anchor="container-interface">
      <name>Container Interface</name>
      <t>Each participating DAP implementation may provide one or more container images,
one for each protocol role it implements. (client, leader, helper, and
collector) A list of available container images will be maintained for each
role. Implementations may want to submit a single aggregator image in both the
leader list and helper list. The test runner will fetch each container using the
given repository, image name, and tag.</t>
      <t>When the container's entry point executable is run, it <bcp14>SHALL</bcp14> start up an HTTP
server listening on port 8080. In all cases, the container will serve the
endpoints described in <xref target="test-api"/> (particularly, the subsection or
subsections appropriate to its protocol role). In the case of a helper or
leader container, it <bcp14>SHALL</bcp14> also serve the endpoints specified by <xref target="DAP"/> on a
port (which <bcp14>MAY</bcp14> be the same port 8080 as used to serve the interoperation test
API) at some relative path. The container should run indefinitely, and the test
runner will terminate the container on completion of the test case. (While DAP
requires HTTPS connections, only using HTTP between containers simplifies test
setup. Putting TLS client/server interop out-of-scope for these tests is
acceptable, as it's not of interest.)</t>
      <t>Log output <bcp14>SHOULD</bcp14> be captured into the directory "/logs" inside the container.
This will be copied out to the host for inspection on completion of the test
case.</t>
      <t>No environment variables or volume mounts will be provided to the containers.</t>
    </section>
    <section anchor="test-api">
      <name>Interoperation Test API</name>
      <t>Each container will have an HTTP server listening on port 8080 for commands from
the test runner. All requests <bcp14>MUST</bcp14> use the HTTP method POST. Requests and
responses for each endpoint listed below <bcp14>SHALL</bcp14> be encoded JSON objects
<xref target="RFC8729"/>, with media type <tt>application/json</tt>. All binary blobs (i.e. task
IDs, batch IDs, HPKE configurations, and verification keys) <bcp14>SHALL</bcp14> be encoded as
strings with base64url <xref target="RFC4648"/>, inside the JSON objects. Any integer values
in the parameters, measurement, or aggregate result of a <xref target="VDAF"/> will be
encoded as strings in base 10 instead of as numbers. This avoids
incompatibilities due to limitations on the range of JSON numbers that different
implementations can process.</t>
      <t>Each of these test APIs should return a status code of 200 OK if the command was
received, recognized, and parsed successfully, regardless of whether any
underlying DAP request succeeded or failed. The DAP-level success or failure
will be included in the test API response body. If a request is made to an
endpoint starting with "/internal/test/", but not listed here, a status code of
404 Not Found <bcp14>SHOULD</bcp14> be returned, to simplify the introduction of new test APIs.</t>
      <section anchor="common-structures">
        <name>Common Structures</name>
        <section anchor="vdaf">
          <name>VDAF</name>
          <t>In multiple APIs defined below, the test runner will send the name of a <xref target="VDAF"/>,
along with the parameters necessary to fully specify the VDAF. These will be
stored in a nested object, with the following attributes (new <tt>type</tt> values and
new keys will be added as new VDAFs are defined).</t>
          <table anchor="vdaf-object">
            <name>VDAF JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>type</tt></td>
                <td align="left">One of <tt>"Prio3Aes128Count"</tt>, <tt>"Prio3Aes128CountVec"</tt>, <tt>"Prio3Aes128Sum"</tt>, or <tt>"Prio3Aes128Histogram"</tt></td>
              </tr>
              <tr>
                <td align="left">
                  <tt>length</tt> (only present if <tt>type</tt> is <tt>"Prio3Aes128CountVec"</tt>)</td>
                <td align="left">The length of the vectors being summed, (encoded in base 10 as a string) used to parameterize the Prio3Aes128CountVec VDAF.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>bits</tt> (only present if <tt>type</tt> is <tt>"Prio3Aes128Sum"</tt>)</td>
                <td align="left">The bit width of the integers being summed, (encoded in base 10 as a string) used to parameterize the Prio3Aes128Sum VDAF.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>buckets</tt> (only present if <tt>type</tt> is <tt>"Prio3Aes128Histogram"</tt>)</td>
                <td align="left">An array of histogram bucket boundaries, (encoded in base 10 as strings) used to parameterize the Prio3Aes128Histogram VDAF.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="query">
          <name>Query</name>
          <t>In multiple APIs defined below, the test runner will need to send a query type,
and in one API, it will need to send a query type along with the associated
query parameters.</t>
          <t>Query types are represented in API requests as numbers, following the values of
the <tt>QueryType</tt> enum in <xref target="DAP"/>.</t>
          <t>Queries are represented in API requests as a nested object, with the following
attributes (new keys will be added as new query types are defined).</t>
          <table anchor="query-object">
            <name>Query JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>type</tt></td>
                <td align="left">A number, representing a query type, as described above.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>batch_interval_start</tt> (only present if <tt>type</tt> is 1, for time interval queries)</td>
                <td align="left">The start of the batch interval, represented as a number equal to the number of seconds since the UNIX epoch.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>batch_interval_duration</tt> (only present if <tt>type</tt> is 1, for time interval queries)</td>
                <td align="left">The duration of the batch interval in seconds, as a number.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>batch_id</tt> (only present if <tt>type</tt> is 2, for fixed size queries)</td>
                <td align="left">A base64url-encoded DAP <tt>BatchID</tt>.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="client">
        <name>Client</name>
        <section anchor="client-ready">
          <name><tt>/internal/test/ready</tt></name>
          <t>The test runner will POST an empty object (i.e. <tt>{}</tt>) to this endpoint to check
if the client container is ready to serve requests. If it is ready, it <bcp14>MUST</bcp14>
return a status code of 200 OK.</t>
        </section>
        <section anchor="upload">
          <name><tt>/internal/test/upload</tt></name>
          <t>Upon receipt of this command, the client container will construct a DAP
report with the given configuration and measurement, and submit it. The client
container will send its response to the test runner once report submission has
either succeeded or permanently failed.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader</tt></td>
                <td align="left">The leader's endpoint URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>helper</tt></td>
                <td align="left">The helper's endpoint URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf</tt></td>
                <td align="left">An object, with the layout given in <xref target="vdaf-object"/>. This determines the VDAF to be used when constructing a report.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>measurement</tt></td>
                <td align="left">If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128Count"</tt>: <tt>"0"</tt> or <tt>"1"</tt>. If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128CountVec"</tt>: an array of strings, each of which is <tt>"0"</tt> or <tt>"1"</tt>. If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128Sum"</tt>: a string (representing an integer in base 10). If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128Histogram"</tt>: a string (representing an integer in base 10).</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>time</tt> (optional)</td>
                <td align="left">If present, this provides a substitute time value that should be used when constructing the report. If not present, the current system time should be used, as per normal. The time is represented as a number, with a value of the number of seconds since the UNIX epoch.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>time_precision</tt></td>
                <td align="left">A number, providing the precision in seconds of report timestamps.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the report was submitted to the leader successfully, or <tt>"error"</tt> otherwise.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="aggregator-leader-or-helper">
        <name>Aggregator (Leader or Helper)</name>
        <section anchor="aggregator-ready">
          <name><tt>/internal/test/ready</tt></name>
          <t>The test runner will POST an empty object (i.e. <tt>{}</tt>) to this endpoint to check
if the aggregator container is ready to serve requests. If it is ready, it <bcp14>MUST</bcp14>
return a status code of 200 OK.</t>
        </section>
        <section anchor="endpoint-for-task">
          <name><tt>/internal/test/endpoint_for_task</tt></name>
          <t>Request the base URL for DAP endpoints for a new task. This API will be invoked
immediately before <tt>/internal/test/add_task</tt> (see <xref target="aggregator-add-task"/>), to
determine the endpoint URLs of the aggregators. If the aggregator uses a common
set of DAP endpoints for all tasks, it could always return the same value, such
as the relative URL <tt>/</tt>. Alternately, implementations may wish to generate new
endpoints for each task, derive the endpoint based on the <tt>TaskId</tt>, etc.</t>
          <t>The test runner will provide the hostname at which the aggregator is externally
reachable. If the aggregator returns a relative URL, the test runner will
combine it with the hostname into an absolute URL, assuming that the port is
8080. Otherwise, the aggregator can incorporate the hostname into an absolute
URL and return that.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>role</tt></td>
                <td align="left">Either <tt>"leader"</tt> or <tt>"helper"</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>hostname</tt></td>
                <td align="left">This aggregator's hostname in the interoperation test environment. This may optionally be used in constructing the endpoint URL as an absolute URL.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the endpoint was successfully selected or set up, or <tt>"error"</tt> otherwise.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>endpoint</tt></td>
                <td align="left">A relative or absolute URL, specifying the DAP aggregator endpoint that should be used for this task. If the test runner receives a relative URL, it will transform it into an absolute URL before performing the next phase of task setup.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="aggregator-add-task">
          <name><tt>/internal/test/add_task</tt></name>
          <t>Register a task with the aggregator, with the given configuration and secrets.</t>
          <t>The HPKE keypair generated for this task should use the mandatory-to-implement
algorithms in section 6 of <xref target="DAP"/>, for broad compatibility.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader</tt></td>
                <td align="left">The leader's endpoint URL. The test runner will ensure this is an absolute URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>helper</tt></td>
                <td align="left">The helper's endpoint URL. The test runner will ensure this is an absolute URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf</tt></td>
                <td align="left">An object, with the layout given in <xref target="vdaf-object"/>. This determines the task's VDAF.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader_authentication_token</tt></td>
                <td align="left">The authentication token that is shared with the other aggregator, as a string. This string <bcp14>MUST</bcp14> be safe for use as an HTTP header value. When the leader sends HTTP requests to the helper, it <bcp14>MUST</bcp14> include this value in a header named <tt>DAP-Auth-Token</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collector_authentication_token</tt> (only present if <tt>role</tt> is <tt>"leader"</tt>)</td>
                <td align="left">The authentication token that is shared between the leader and collector, as a string. This string <bcp14>MUST</bcp14> be safe for use as an HTTP header value. When the collector sends HTTP requests to the leader, it <bcp14>MUST</bcp14> include this value in a header named <tt>DAP-Auth-Token</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>role</tt></td>
                <td align="left">Either <tt>"leader"</tt> or <tt>"helper"</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>verify_key</tt></td>
                <td align="left">The verification key shared by the two aggregators, encoded with base64url.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>max_batch_query_count</tt></td>
                <td align="left">A number, providing the maximum number of times any report can be included in a collect request.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>query_type</tt></td>
                <td align="left">A number, representing the task's query type, as described in <xref target="query"/>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>min_batch_size</tt></td>
                <td align="left">A number, providing the minimum number of reports that must be in a batch for it to be collected.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>max_batch_size</tt> (only present if <tt>query_type</tt> is 2, for fixed size queries)</td>
                <td align="left">A number, providing the maximum number of reports that may be in a batch for it to be collected.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>time_precision</tt></td>
                <td align="left">A number, providing the precision in seconds of report timestamps. For tasks using the time interval query type, the batch interval's duration will always be a multiple of this value.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collector_hpke_config</tt></td>
                <td align="left">The collector's HPKE configuration, encoded in base64url, for encryption of aggregate shares.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>task_expiration</tt></td>
                <td align="left">A number, providing the time when clients are no longer expected to upload to this task. This is represented as a number of seconds since the UNIX epoch.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the task was successfully set up, or <tt>"error"</tt> otherwise. (for example, if the VDAF was not supported)</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="leader">
        <name>Leader</name>
        <section anchor="fetch-batch-ids">
          <name><tt>/internal/test/fetch_batch_ids</tt></name>
          <t>Retrieve the identifiers of every batch generated by the leader from a task's
reports. This is only applicable to tasks with a fixed size query type.</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>
            </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 and its batch IDs were successfully looked 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>batch_ids</tt> (if successful)</td>
                <td align="left">An array of strings, where each string is a base64url-encoded DAP <tt>BatchID</tt>.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="collector">
        <name>Collector</name>
        <section anchor="collector-ready">
          <name><tt>/internal/test/ready</tt></name>
          <t>The test runner will POST an empty object (i.e. <tt>{}</tt>) to this endpoint to check
if the collector container is ready to serve requests. If it is ready, it <bcp14>MUST</bcp14>
return a status code of 200 OK.</t>
        </section>
        <section anchor="collector-add-task">
          <name><tt>/internal/test/add_task</tt></name>
          <t>Register a task with the collector, with the given configuration. Returns the
collector's HPKE configuration for this task.</t>
          <t>The HPKE keypair generated for this task should use the mandatory-to-implement
algorithms in section 6 of <xref target="DAP"/>, for broad compatibility.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader</tt></td>
                <td align="left">The leader's endpoint URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf</tt></td>
                <td align="left">An object, with the layout given in <xref target="vdaf-object"/>. This determines the task's VDAF.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collector_authentication_token</tt></td>
                <td align="left">The authentication token that is shared between the leader and collector, as a string. This string <bcp14>MUST</bcp14> be safe for use as an HTTP header value. When the collector sends HTTP requests to the leader, it <bcp14>MUST</bcp14> include this value in a header named <tt>DAP-Auth-Token</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>query_type</tt></td>
                <td align="left">A number, representing the task's query type, as described in <xref target="query"/>.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the task was successfully set up, or <tt>"error"</tt> otherwise. (for example, if the VDAF was not supported)</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collector_hpke_config</tt> (if successful)</td>
                <td align="left">The collector's HPKE configuration, encoded in base64url, for encryption of aggregate shares.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="collect-start">
          <name><tt>/internal/test/collect_start</tt></name>
          <t>Send a collect request to the leader with the provided parameters, and return a
handle to the test runner identifying this collect request. The test runner will
provide this handle to the collector in subsequent <tt>/internal/test/collect_poll</tt>
requests (see <xref target="collect-poll"/>).</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>task_id</tt></td>
                <td align="left">A base64url-encoded DAP <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>agg_param</tt></td>
                <td align="left">A base64url-encoded aggregation parameter.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>query</tt></td>
                <td align="left">An object, with the layout given in <xref target="query-object"/>. This provides the collect request's query, and in turn determines which reports should be included.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the collect request succeeded, or <tt>"error"</tt> otherwise.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>handle</tt> (if successful)</td>
                <td align="left">A handle produced by the collector to refer to this collect request. This must be a string.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="collect-poll">
          <name><tt>/internal/test/collect_poll</tt></name>
          <t>Upon receiving this command, the collector will poll the leader's collect URL
for the collect job associated with the provided handle, and provide the status
and result to the test runner.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>handle</tt></td>
                <td align="left">The handle for a collect request from a previous invocation of <tt>/internal/test/collect_start</tt>. (see <xref target="collect-start"/>)</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">Either <tt>"complete"</tt> if the result was returned, <tt>"in progress"</tt> if the result was 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>report_count</tt> (if complete)</td>
                <td align="left">A number, reflecting the count of client reports included in this aggregated result.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>result</tt> (if complete)</td>
                <td align="left">The result of the aggregation. If the VDAF is of type Prio3Aes128Count or Prio3Aes128Sum, this will be a string, representing an integer in base 10. If the VDAF is of type Prio3Aes128CountVec or Prio3Aes128Histogram, this will be an array of strings, each representing an integer in base 10.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="heavy-hitters">
          <name>Heavy Hitters</name>
          <t>Once Poplar1 reaches a future draft of <xref target="DAP"/>, additional test APIs for collector
containers should be introduced to perform an entire Heavy Hitters computation
on a given Poplar1 task and collection interval, encompassing multiple collect
flows automatically initiated by the collector.</t>
        </section>
      </section>
      <section anchor="test-cases">
        <name>Test Cases</name>
        <t>Test cases could be written to cover the following scenarios.</t>
        <ul spacing="normal">
          <li>Test successful aggregations with each VDAF.</li>
          <li>Test an aggregation over a few hundred or thousand reports, to exercise the
aggregators' division of reports into aggregation jobs.</li>
          <li>Test that uploading a report with a time far in the future is rejected.</li>
          <li>Confirm that leaders and helpers reject requests with respective
authentication tokens that are incorrect.</li>
          <li>Test enforcement of <tt>max_batch_query_count</tt> by making overlapping collect
requests.</li>
          <li>Perform an entire aggregation and collect flow, attempt to upload a late
report that falls into the same collect interval, and test that performing the
collect request a second time yields the same result.</li>
          <li>Attempt to upload a canned report from the test runner more than once, and
confirm that anti-replay measures were effective by inspecting the aggregation
result.</li>
        </ul>
      </section>
      <section anchor="other-test-considerations">
        <name>Other Test Considerations</name>
        <t>All test cases should automatically fail after a generous timeout.</t>
        <t>It is the responsibility of the test runner to wait for all containers to start
up and respond successfully to a request to <tt>/internal/test/ready</tt> before
sending any further commands.</t>
        <t>Aggregator URLs will be constructed by the test runner with hostnames that
resolve to the respective containers within the container network.</t>
        <t>Once a future <xref target="DAP"/> draft solves the issue of retries in the aggregate flow, a
reverse proxy could be introduced in front of each aggregator to inject failures
when sending requests or responses, to test the protocol's resilience. (It is
known such a test would fail with the current protocol.)</t>
      </section>
      <section anchor="test-runner-operation">
        <name>Test Runner Operation</name>
        <t>The following sequence outlines how the test runner will use the above APIs on
port 8080 of each container to perform a typical integration test, executing a
successful aggregation.</t>
        <ol spacing="normal" type="1"><li>Create and start containers.</li>
          <li>Set up networking between containers.</li>
          <li>Try sending <tt>/internal/test/ready</tt> requests to each container, and retry
until they succeed.</li>
          <li>Generate a random <tt>TaskId</tt>, random authentication tokens, and a VDAF
verification key.</li>
          <li>Send a <tt>/internal/test/endpoint_for_task</tt> request (<xref target="endpoint-for-task"/>) to
the leader.</li>
          <li>Send a <tt>/internal/test/endpoint_for_task</tt> request (<xref target="endpoint-for-task"/>) to
the helper.</li>
          <li>Construct aggregator URLs using the above responses.</li>
          <li>Send a <tt>/internal/test/add_task</tt> request (<xref target="collector-add-task"/>) to the
collector. (the collector generates an HPKE key pair as a side-effect)</li>
          <li>Send a <tt>/internal/test/add_task</tt> request (<xref target="aggregator-add-task"/>) to the
leader.</li>
          <li>Send a <tt>/internal/test/add_task</tt> request (<xref target="aggregator-add-task"/>) to the
helper.</li>
          <li>Send one or more <tt>/internal/test/upload</tt> requests (<xref target="upload"/>) to the client.</li>
          <li>If the task has a fixed size query type, send a
<tt>/internal/test/fetch_batch_ids</tt> request (<xref target="fetch-batch-ids"/>) to the leader.</li>
          <li>Send one or more <tt>/internal/test/collect_start</tt> requests (<xref target="collect-start"/>)
to the collector. (this provides a handle for use in the next step)</li>
          <li>Send <tt>/internal/test/collect_poll</tt> requests (<xref target="collect-poll"/>) to the
collector, polling until each collection is completed. (the collector will
provide the calculated aggregate results)</li>
          <li>Stop containers.</li>
          <li>Copy logs out of each container.</li>
          <li>Delete containers, and clean up container networking resources.</li>
        </ol>
      </section>
    </section>
    <section anchor="implementation-status">
      <name>Implementation Status</name>
      <t><xref target="Janus"/> and <xref target="divviup-ts"/> 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="22" month="September" year="2022"/>
            <abstract>
              <t>   There are many situations in which it is desirable to take
   measurements of data which people consider sensitive.  In these
   cases, the entity taking the measurement is usually not interested in
   people's individual responses but rather in aggregated data.
   Conventional methods require collecting individual responses and then
   aggregating them, thus representing a threat to user privacy and
   rendering many such measurements difficult and impractical.  This
   document describes a multi-party distributed aggregation protocol
   (DAP) for privacy preserving measurement (PPM) which can be used to
   collect aggregate data without revealing any individual user's data.

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

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-vdaf-03"/>
        </reference>
        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner">
              <organization/>
            </author>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification.  These words are often capitalized. This document defines these words as they should be interpreted in IETF documents.  This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba">
              <organization/>
            </author>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol  specifications.  This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the  defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
        <reference anchor="RFC8729">
          <front>
            <title>The RFC Series and RFC Editor</title>
            <author fullname="R. Housley" initials="R." role="editor" surname="Housley">
              <organization/>
            </author>
            <author fullname="L. Daigle" initials="L." role="editor" surname="Daigle">
              <organization/>
            </author>
            <date month="February" year="2020"/>
            <abstract>
              <t>This document describes the framework for an RFC Series and an RFC Editor function that incorporate the principles of organized community involvement and accountability that has become necessary as the Internet technical community has grown, thereby enabling the RFC Series to continue to fulfill its mandate. This document obsoletes RFC 4844.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="8729"/>
          <seriesInfo name="DOI" value="10.17487/RFC8729"/>
        </reference>
        <reference anchor="RFC4648">
          <front>
            <title>The Base16, Base32, and Base64 Data Encodings</title>
            <author fullname="S. Josefsson" initials="S." surname="Josefsson">
              <organization/>
            </author>
            <date month="October" year="2006"/>
            <abstract>
              <t>This document describes the commonly used base 64, base 32, and base 16 encoding schemes.  It also discusses the use of line-feeds in encoded data, use of padding in encoded data, use of non-alphabet characters in encoded data, use of different encoding alphabets, and canonical encodings.  [STANDARDS-TRACK]</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="4648"/>
          <seriesInfo name="DOI" value="10.17487/RFC4648"/>
        </reference>
      </references>
      <references>
        <name>Informative References</name>
        <reference anchor="SI2020" target="https://research.protocol.ai/publications/automating-quic-interoperability-testing/seemann2020.pdf">
          <front>
            <title>Automating QUIC Interoperability Testing</title>
            <author initials="M." surname="Seemann">
              <organization/>
            </author>
            <author initials="J." surname="Iyengar">
              <organization/>
            </author>
            <date year="2020" month="August" day="10"/>
          </front>
        </reference>
        <reference anchor="Janus" target="https://github.com/divviup/janus">
          <front>
            <title>Experimental implementation of the DAP specification</title>
            <author>
              <organization/>
            </author>
            <date year="2022" month="August" day="25"/>
          </front>
        </reference>
        <reference anchor="divviup-ts" target="https://github.com/divviup/divviup-ts">
          <front>
            <title>TypeScript client for https://divviup.org</title>
            <author>
              <organization/>
            </author>
            <date year="2022" month="October" day="05"/>
          </front>
        </reference>
        <reference anchor="REF-IMPL" target="https://github.com/divergentdave/dap-interop-test-runner">
          <front>
            <title>Reference DAP interoperation test harness</title>
            <author>
              <organization/>
            </author>
            <date year="2022" month="October" day="04"/>
          </front>
        </reference>
      </references>
    </references>
    <section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>Thanks to Brandon Pitman, Christopher Patton, and Tim Geoghegan for feedback and
contributions.</t>
    </section>
  </back>
  <!-- ##markdown-source:
H4sIAAAAAAAAA+1c63LcuJX+j6fA9vywlOpuSY43mag2W1FkT6zEF42lSTY1
NaVGk+hujNkkhyBb7siqymts1W7VPss+Sp5kzwUAQTYlyzPjzWxt5s9YbBI4
ODjnO1dgMpmI2tSZPpajpyfn8iyvdVWUulK1KXJ5qW0tn2prlvlIJKrWy6La
HkuTLwoh0iLJ1Rq+TCu1qCdpUhRvJ2W5nqSqnBgeaFLDCJOURpgcPha2ma+N
tTB2vS3h07Nnl18IU1bHsq4aWz8+PPwVvJU367mujkUKMx6LpMitzm1jj+VC
ZVaLzbH8uVCVVkD0hU6aytTbkbguqrfLqmhKeHpemY1KtvK80lZXG5Mv5Uut
bFPptc7rkdjovIGRpXzoB1IyvaM/wSz46+/wQ3y+ViaD57Du3xhdL6ZFtcTH
qkpW8HhV16U9PjjAt/CR2eipf+0AHxzMq+La6gP4/gC/W5p61czhyxRerZYw
eao2+uCBLMYRMoUPork7I015gqkpHjrmQ9+brup1NhJCNfWqgM2TEyBGykWT
ZSwmT9XGpPIUhqEfgAMqN38hQQNBuHjzO3qsHUNpvt8A7RvTlMxVkRfVGt7f
0NaBuMJnk6dTpg+5GsgDGZLyj09Pvui8UcEbyaJaTjapWkwOfy4ECnI05MXZ
48PHh8dEh9eKk6Yu8A3Y8y+/OjuNNGRuMhA80hH4dURfkcRKHGVy+Pnk6JCH
UsD+eEdQyHDzp2VV1EVSZFNlDspmnpmE2GEPVJh18l1jEs/vMCsxHn49sBoY
luc447RMF0xF2AL6bwL6Crrzciov+OXu899P5dlW50tVwfPfqxz0LGbAs3cw
q0EtUJk06zIjjWB4KBayXmncCWlLnZiFo7/LisfIisf/3GGF54STxqRYH7it
PvgWSYCX3d+TukvP6BIU8SKpTFnLJDNAi4Q9lJGwtwLTI+MINuXBZLTTwxdv
nn0xOXt5/qJLyRu90JXOE+aA6UInbpBcqSrX1g5S8uQBlMQI0Ne6qslzXQkh
JpOJVHNbVyqphbhcGSsBmhvcJpnqhQEKpJIw4tqTReMsFBCOrOtuqnW7Kp4a
GNLMm1qn8mS5rPSSF3buZJa+vR835R4wZl+qPBWAEbBncyBlVVzD+EBkj5RE
5XKuZWNhvrqQwEjUzSG2wixirutrrXOSv94CpqSSsOZKA8gkK42swVVcA2+B
D/Ae8KSyYyRM5vpaED+LPNvKk/Mz/hCmrYq0SRwxVbExaLVop2tl31r62OSm
NjA2vpDARgNlU7cja5OmmRbiM0QMGoo046e1P1/Dcr4BfgFNoj+zShBp0JgM
7YGVfgdSsyA1qGWpqtokpiTcEqQT/Z05scTBlbJAXlPBx7idSS09EsqqyDRs
zR7r9lhmWqUg5sqtsKjGcqUzoEXGj3Az4OtMJ/DnPhjlvEG4ul92VJYh2+cm
bzmrN/iDtGv8kf0QfDywGPi0yVJgg6jVO9x4YKOWywLmpR2KtvmRvWN7UQsK
qXM1B1HxmB+A1epBzo+lLeBnVd/J+h1KWbX0u2Sl8iXIC4qGVslKFjCNYx9q
kgVDzTTaxtReJ0WlEW1kEW92n3E7W32BT8xiizLXXYZwGwEKCUxWBvVImhS+
da/rqioqS0+7o47j14QCCpZApyERpQUEMerYI15gpUEJcXiQmkqTE4rf9bkF
Cnyp3jLVtjSO84uqWMsvwRQ7+/+G0Fd+zS7DN2Nm533bwCACOAh8RGCS6wJw
JsARLFUtEQlS2BGcHQTDo4IIEgMaxPvD6M8ctGBEahq1HS3scOBIoA20Cn3q
BfAOCMhBGZxPG2NqHybBA0/lRlWmaKx8fnl5jlAJnPyuQZGcClDskqhwoh/J
exAAXMCYRqcBEJB0FbaunVHwqpqyLGBEW6yRymvZorSfH2i7XhlYJAtSlpFp
0R0O1YVo0duCQUZEBwBHckHaa6SAF5hUGpULoOla43Cgm5VZLnEMVA1m8kK0
KgDBzMSzF8UGsP60yDcooChaOOpTxHdDfyP0a/lWbyXwO7Vy9PKri8vRmP8v
X72mf795Bk7mm2dP8d8Xz09evAj/EO6Ni+evv3rxtP1X++Xp65cvn716yh/D
U9l5JEYvT/484s0cvT6/PHv96uTFiJkfWyQ0fiB6c4c9JTAFOKZssOCkrb89
Pf/v/zp6Im9u/unNF6ePj45+dXvr/vj86JdP4I/rlXZ6R1vGfwIjt0KVJXi/
OAoBsCrBymTMeAvOQQ74Xmlg58++Rs58cyz/ZZ6UR0/+1T3ABXceep51HhLP
dp/sfMxMHHg0ME3gZud5j9Ndek/+3Pnb8z166MXG6e2ZVxQhnu0gyoAZAlO3
9chyL7CMBf66Cwtob6Wp22EBunvG11tc2k4RmdkTmYH9Rp1XGwxwwYrtAhrp
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
p/LDnTJg6k0dWuciBMSMHrongm5zSN1g3SOqfLVPFAHekaXkrhSBORE2CLCK
piI2+LPeQHjUEUqNge15eNfmFFVYOzEf6JhvrWJVwpPeRbYJYV+rgfEK8cP+
lT3+FqGpMy/BnPB1DWxUaGxmsbGWm5MrKhyEO4DayNypEJCEdwSR//pu2wJw
ZF4MXc7Eak5AGnUW4QUVOWGLO7VrBZWzPEsD4lAbnzvmTjBa+7ZQf7nFIzoR
YdDbwWuY9khexNscL6zBZkhEQ/zmmkgkaWtzqa7VO1z3uB+ZGXeT1OtwOxYl
RSNjQsE2MBXkNKMQcjVwzRHtus+O0uErNtUwXntLgOdQu2+xbUa/BpWF3Y6o
u27sLi8hIRTDlgx2/mgqT0Fya82NR3RGK74L4QgvoKRrTgbunOq9eFltwybd
oR1xfrC7qpApqbZ4xyIAvaEYauujXprid771VOGh9xRQru0idQ8GrQ6PrvjM
Mwzf72pwC6V3HtCY7GFg7+Zmt0P5FgsMOEkbAX6y4dkY0/Cn7YmjHri0JXKW
saA095HVFiEiagaqEbeunIIGLXLJ5F43LvYlAk4ru+KBpOoBJ6bBNEzYyOx/
LFXDzdgRWR/ehO83bsR9Gje+yumus2Vtgu3mxp0yC2O6uIwGPIuSySti0WDB
dOxOzyI5H6wCR2vrl4NbGvrMum9RvQRqvLZ+3E8S28tMkpR0D+9ECYnGhu5h
auq0tS5b2bg/hTNEiUthDonrmFI2qCWMOw6a2rjFhoA33RFtSsTCaHFiBhAZ
r2aqo6Sm9+Asr6Euyj5+nhblliJ8ug9nB/fpnacaidi56S+BXcsRpXesO1tM
WzTgWPu7bbqXkV1wEkl8TfcIf0Pjfd3epPuNt4QYg/kv8awSTB7Owu7cDDsV
X/v7d7/hsnEVLt7dvZBYdcyixytj4/HESRvVDt3seR3iPFWt8fI6ncGe0alF
WLO/cnzXDQUHbeCGNnaH06Kkjie3vM6liN5jDY2k3Offv5e2LT1hNR2LXXjD
badhC3yRYlFf4w9t9zRPJ8qmKgvyb+je24JeL9srP/ggmt/Yk1cnOwvs3l27
omwXv6kSf3cmXX47V8lbHOUkQQcp0+mSbpYTN8ecTtLpr0d0l/uICu8qf0tm
/LdkdyHuNzV4t2N5uqowh1Kix3sO4ZS/yfPSrMF8F8sVKAPXlBdg1XFSd0Nd
zufgmaT/AY6WVz7kXgAA

-->

</rfc>
