<?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.7.5 (Ruby 3.2.2) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-ramseyer-grow-peering-api-02" category="info" consensus="true" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.19.1 -->
  <front>
    <title>Peering API</title>
    <seriesInfo name="Internet-Draft" value="draft-ramseyer-grow-peering-api-02"/>
    <author fullname="Jenny Ramseyer">
      <organization>Meta</organization>
      <address>
        <email>ramseyer@meta.com</email>
      </address>
    </author>
    <date year="2024" month="January" day="19"/>
    <keyword>BGP</keyword>
    <keyword>peering</keyword>
    <keyword>configuration</keyword>
    <abstract>
      <?line 37?>

<t>We propose an API standard for BGP Peering, also known as interdomain interconnection through global Internet Routing.
This API offers a standard way to request public (settlement-free) peering, verify the status of a request or BGP session, and list potential connection locations.
The API is backed by PeeringDB OIDC, the industry standard for peering authentication.
We also propose future work to cover private peering, and alternative authentication methods.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://bgp.github.io/draft-ietf-peering-api/draft-peering-api-ramseyer-protocol.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-ramseyer-grow-peering-api/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/bgp/draft-ietf-peering-api"/>.</t>
    </note>
  </front>
  <middle>
    <?line 44?>

<section anchor="introduction">
      <name>Introduction</name>
      <t>The Peering API is a mechanism that allows networks to automate interdomain interconnection  between two Autonomous Systems (AS) through the Border Gateway Protocol 4 (BGP-4) (<eref target="https://datatracker.ietf.org/doc/html/rfc4271">RFC4271</eref>).
Using the API, networks will be able to automatically request and accept peering interconnections between Autonomous Systems in public or private scenarios in a time faster than it would take to configure sessions manually.
By speeding up the peering turn-up process and removing the need for manual involvement in peering, the API and automation will ensure that networks can get interconnected as fast, reliably, cost-effectively, and efficiently as possible.
As a result, this improves end-user performance for all applications using networks interconnection supporting the Peering API.</t>
      <t>Business Justification:</t>
      <t>By using the Peering API, entities requesting and accepting peering can significantly improve the process to turn up interconnections by:</t>
      <ul spacing="normal">
        <li>
          <t>Reducing in person-hours spent configuring peering</t>
        </li>
        <li>
          <t>Reducing configuration mistakes by reducing human interaction</t>
        </li>
        <li>
          <t>And by peering, reducing network latency through expansion of interconnection relationships</t>
        </li>
      </ul>
    </section>
    <section anchor="conventions-and-definitions">
      <name>Conventions and Definitions</name>
      <t>All terms used in this document will be defined here:</t>
      <ul spacing="normal">
        <li>
          <t>Initiator: Network that wants to peer</t>
        </li>
        <li>
          <t>Receiver: Network that is receiving communications about peering</t>
        </li>
        <li>
          <t>Configured: peering session that is set up on one side</t>
        </li>
        <li>
          <t>Established: session is already defined as per BGP-4 specification (<eref target="https://datatracker.ietf.org/doc/html/rfc4271#page-71">page 71</eref>)</t>
        </li>
      </ul>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>As peering connections exchange real Internet traffic, this API requires a security component to verify that the requestor is authorized to operate the interconnection on behalf of such AS.
In this initial proposal, the API follows an authorization model based on OpenID Connect (OIDC) (<eref target="https://openid.net/specs/openid-connect-core-1_0.html">OpenID.Core</eref>) and OAuth 2.0 (<eref target="https://datatracker.ietf.org/doc/html/rfc6749">RFC6749</eref>) where the Authorization Server is PeeringDB. The choice of OpenID Connect is to use the standardized token exchange format based on JSON Web Tokens (<eref target="https://www.rfc-editor.org/rfc/rfc7519.html">RFC7519</eref>) which allows interoperation with existing web-based application flows. JWT tokens also supply sufficient claims to implement receiver-side authorization decisions when used as bearer access tokens (<eref target="https://datatracker.ietf.org/doc/html/rfc9068">RFC9068</eref>) and for which best common practices also exist (<eref target="https://www.rfc-editor.org/rfc/rfc8725.html">RFC8725</eref>).
After further discussion, the authors decided to offer alternate authentication options to accommodate the security concerns of different parties.
As peers may require varying security standards, this document proposes to support PeeringDB OIDC as the base requirement, with optional security extensions in addition (RPKI (<eref target="https://datatracker.ietf.org/doc/html/rfc6480">RFC6480</eref>) or alternative OIDC Authorization Servers, for example).
This document hopes that, through the RFC process, the Working Group can come to a consensus on a base "authorization standard," to ease adoption for peering participants.</t>
      <t>Of particular interest is RPKI.
PeeringDB OIDC allows the API to identify who the requesting party is, while RPKI-signing allows such requesting party to prove that they own some of the Internet-assigned resources referenced in the request.
This combination provides a low entry barrier to create an identity federation across the participating ASs' API with a stronger guarantee of resource ownership against potential for misattribution and repudiation.
The authors recognize that not all partners have the time or engineering resources to support all authorization standards, so the API reference implementations will offer an extensible security mechanism to meet varying identity and security requirements.
For RPKI-based authentication, this document refers to RPKI Signed Checklists (RSCs) (<eref target="https://datatracker.ietf.org/doc/html/rfc9323">RFC9323</eref>).</t>
    </section>
    <section anchor="protocol">
      <name>Protocol</name>
      <t>The Peering API follows the Representational State Transfer (<eref target="http://roy.gbiv.com/pubs/dissertation/top.htm">REST</eref>) architecture where sessions, locations, and maintenance events are the resources the API represents and is modeled after the OpenAPI standard <eref target="https://swagger.io/specification/">OpenAPI-v3.1.0</eref>.
Using the token bearer model (<eref target="https://datatracker.ietf.org/doc/html/rfc6750">RFC6750</eref>), a client application can request to add or remove peering sessions, list potential interconnection locations, and query for upcoming maintenance events on behalf of the AS resource owner.</t>
    </section>
    <section anchor="example-request-flow">
      <name>Example Request Flow</name>
      <t>For a diagram, please see: https://github.com/bgp/autopeer/blob/main/README.md#sequence-diagram</t>
      <section anchor="auth">
        <name>AUTH</name>
        <t>First, the initiating OAuth2 Client is also the Resource Owner (RO) so it can follow the OAuth2 client credentials grant <eref target="https://datatracker.ietf.org/doc/html/rfc6749#section-4.4">RFC6749-section-4.4</eref>.
In this example, the client will use PeeringDB OIDC credentials to acquire a JWT access token that is scoped for use with the receiving API.
On successful authentication, PeeringDB provides the Resource Server (RS) with the client's email (for potential manual discussion), along with the client's usage entitlements (known as OAuth2 scopes), to confirm the client is permitted to make API requests on behalf of the initiating AS.</t>
      </section>
      <section anchor="request">
        <name>REQUEST</name>
        <ol spacing="normal" type="1"><li>
            <t>ADD SESSION (CLIENT REQUEST)</t>
          </li>
        </ol>
        <ul spacing="normal">
          <li>
            <t>Client provides:
            </t>
            <ul spacing="normal">
              <li>
                <t>Dictionary (multiple):
                </t>
                <ol spacing="normal" type="1"><li>
                    <t>Local ASN (server)</t>
                  </li>
                  <li>
                    <t>Local IP</t>
                  </li>
                  <li>
                    <t>Peer ASN (client)</t>
                  </li>
                  <li>
                    <t>Peer IP</t>
                  </li>
                  <li>
                    <t>Peer Type (public or private)</t>
                  </li>
                  <li>
                    <t>MD5 (optional)</t>
                  </li>
                  <li>
                    <t>IXP ID (PeeringDB Identifier)</t>
                  </li>
                  <li>
                    <t>Status
  9: UUID</t>
                  </li>
                  <li>
                    <t>Sent prefixes (0 if not Established) (optional)</t>
                  </li>
                  <li>
                    <t>Received prefixes (0 if not Established) (optional)</t>
                  </li>
                  <li>
                    <t>Accepted Prefixes (optional)</t>
                  </li>
                </ol>
              </li>
            </ul>
          </li>
          <li>
            <t>Server actions:
            </t>
            <ul spacing="normal">
              <li>
                <t>Server confirms requested clientASN in list of authorized ASNs.</t>
              </li>
              <li>
                <t>Optional: checks traffic levels, prefix limit counters, other desired internal checks.</t>
              </li>
            </ul>
          </li>
        </ul>
        <ol spacing="normal" type="1"><li>
            <t>ADD SESSION (SERVER RESPONSE)</t>
          </li>
        </ol>
        <ul spacing="normal">
          <li>
            <t>APPROVAL CASE
            </t>
            <ul spacing="normal">
              <li>
                <t>Server returns dictionary of acceptable peering sessions.  Note: this dictionary may also contain additional sessions on which to peer.  See "Public Peering Session Negotiation" for details.</t>
              </li>
            </ul>
          </li>
          <li>
            <t>REJECTION CASE  </t>
            <ul spacing="normal">
              <li>
                <t>Server returns dictionary of acceptable and rejected sessions.</t>
              </li>
            </ul>
          </li>
          <li>
            <t>Optional information:
            </t>
            <ul spacing="normal">
              <li>
                <t>The server may also include the following details:
                </t>
                <ol spacing="normal" type="1"><li>
                    <t>Prefix limit counters (optional value)</t>
                  </li>
                  <li>
                    <t>TimeWindow: Time window indicating when sessions will be configured after being notified (may be 0 if sessions are already configured on receiver side)</t>
                  </li>
                  <li>
                    <t>isInboundFiltered: optional bool that indicates whether prefixes will be filtered inbound.  If this is set to true, the time window should be set for how long the prefixes will be filtered.</t>
                  </li>
                  <li>
                    <t>isOutboundFiltered: optional bool that indicates whether prefixes will be filtered outbound.  If this is set to true, the time window should be set for how long the prefixes will be filtered.  If the outbound limit is longer than the inbound limit time, the time window should be set to the max of inbound versus outbound.</t>
                  </li>
                </ol>
              </li>
            </ul>
          </li>
        </ul>
      </section>
      <section anchor="client-configuration">
        <name>CLIENT CONFIGURATION</name>
        <t>The client then configures the chosen peering sessions.
If the server added additional approved peering sessions, the client may choose whether or not to configure those sessions.
For every session that the server rejected, the client removes that session from the list to be configured.</t>
      </section>
      <section anchor="server-configuration">
        <name>SERVER CONFIGURATION</name>
        <t>The server configures all sessions that are in its list of approved peering sessions from its reply to the client.</t>
      </section>
      <section anchor="monitoring">
        <name>MONITORING</name>
        <t>Both client and server wait for sessions to establish.
At any point, client may send a "GET STATUS" request to the server, to request the status of the original request (by UUID) or of a session (by session UUID).
The client will send a dictionary along with the request, as follows:</t>
        <ul spacing="normal">
          <li>
            <t>Dictionary:
            </t>
            <ul spacing="normal">
              <li>
                <t>Local ASN (server)</t>
              </li>
              <li>
                <t>Local IP</t>
              </li>
              <li>
                <t>Peer ASN (client)</t>
              </li>
              <li>
                <t>Peer IP</t>
              </li>
              <li>
                <t>Peer Type</t>
              </li>
              <li>
                <t>MD5 (optional)</t>
              </li>
              <li>
                <t>IXP ID</t>
              </li>
              <li>
                <t>Status</t>
              </li>
              <li>
                <t>UUID</t>
              </li>
              <li>
                <t>Sent prefixes (0 if not Established) (optional)</t>
              </li>
              <li>
                <t>Received prefixes (0 if not Established) (optional)</t>
              </li>
              <li>
                <t>Accepted Prefixes (optional)</t>
              </li>
            </ul>
          </li>
        </ul>
        <t>The server then responds with the same dictionary, with the information that it understands (status, etc).</t>
      </section>
      <section anchor="completion">
        <name>COMPLETION</name>
        <t>If both sides report that the session is established, then peering is complete.
If one side does not configure sessions within the server's acceptable configuration window (TimeWindow), then the server is entitled to remove the configured sessions and report "Unestablished" to the client.</t>
      </section>
    </section>
    <section anchor="api-endpoints-and-specifications">
      <name>API Endpoints and Specifications</name>
      <t>Each peer needs a public API endpoint that will implement the API protocol.
This API should be publicly listed in peeringDB and also as a potential expansion of <eref target="https://datatracker.ietf.org/doc/html/rfc9092">RFC9092</eref> which could provide endpoint integration to WHOIS (<eref target="https://datatracker.ietf.org/doc/html/rfc3912">RFC3912</eref>).
Each API endpoint should be fuzz-tested and protected against abuse.  Attackers should not be able to access internal systems using the API.
Every single request should come in with a unique GUID called RequestID that maps to a peering request for later reference.  This GUID format should be standardized across all requests.  This GUID should be provided by the receiver once it receives the request and must be embedded in all communication.  If there is no RequestID present then that should be interpreted as a new request and the process starts again.
An email address is needed for communication if the API fails or is not implemented properly (can be obtained through PeeringDB).</t>
      <t>For a programmatic specification of the API, please see the public Github here: <eref target="https://github.com/bgp/autopeer/blob/main/api/openapi.yaml">https://github.com/bgp/autopeer/blob/main/api/openapi.yaml</eref></t>
      <t>This initial draft fully specifies the Public Peering endpoints.  Private Peering and Maintenance are under discussion, and the authors invite collaboration and discussion from interested parties.</t>
      <section anchor="data-types">
        <name>DATA TYPES</name>
        <t>As defined in <eref target="https://github.com/bgp/autopeer/blob/main/api/openapi.yaml">https://github.com/bgp/autopeer/blob/main/api/openapi.yaml</eref>.
Please see specification for OpenAPI format.</t>
        <t>Peering Location</t>
        <t>Contains string field listing the desired peering location in format <tt>pdb:ix:$IX_ID</tt>, and an enum specifying peering type (public or private).</t>
        <t>Session Status</t>
        <t>Status of BGP Session, both as connection status and approval status (Established, Pending, Approved, Rejected, Down, Unestablished, etc)</t>
        <t>Session Array</t>
        <t>Array of potential BGP sessions, with request UUID.
 Request UUID is optional for client, and required for server.
 Client may provide initial UUID for client-side tracking, but the server UUID will be the final definitive ID.  RequestID will not change across the request.</t>
        <ul spacing="normal">
          <li>
            <t>BGP Session
            </t>
            <ul spacing="normal">
              <li>
                <t>local_asn (ASN of requestor)</t>
              </li>
              <li>
                <t>local_ip (IP of requestor, v4 or v6)</t>
              </li>
              <li>
                <t>peer_asn (server ASN)</t>
              </li>
              <li>
                <t>peer_ip (server-side IP)</t>
              </li>
              <li>
                <t>peer_type (public or private)</t>
              </li>
              <li>
                <t>md5 (optional string)</t>
              </li>
              <li>
                <t>location (Peering Location, as defined above)</t>
              </li>
              <li>
                <t>status (Session Status, as defined above)</t>
              </li>
              <li>
                <t>UUID (of individual session.  Server must provide UUID.  Client may provide initial UUID for client-side tracking, but the server UUID will be the final definitive ID)</t>
              </li>
            </ul>
          </li>
        </ul>
        <t>Error</t>
        <t>API Errors, for field validation errors in requests, and request-level errors.</t>
        <t>The above is sourced largely from the linked OpenAPI specification.</t>
      </section>
      <section anchor="endpoints">
        <name>Endpoints:</name>
        <t>(As defined in <eref target="https://github.com/bgp/autopeer/blob/main/api/openapi.yaml">https://github.com/bgp/autopeer/blob/main/api/openapi.yaml</eref>)
On each call, there should be rate limits, allowed senders, and other optional restrictions.</t>
        <section anchor="public-peering-over-an-internet-exchange-ix">
          <name>Public Peering over an Internet Exchange (IX):</name>
          <ul spacing="normal">
            <li>
              <t>/add_sessions: ADD/AUGMENT IX PEER
              </t>
              <ul spacing="normal">
                <li>
                  <t>Establish new BGP sessions between peers, at the desired exchange.</t>
                </li>
                <li>
                  <t>Below is based on OpenAPI specification: <eref target="https://github.com/bgp/autopeer/blob/main/api/openapi.yaml">https://github.com/bgp/autopeer/blob/main/api/openapi.yaml</eref></t>
                </li>
                <li>
                  <t>POST: /add_sessions
                  </t>
                  <ul spacing="normal">
                    <li>
                      <t>Request body: Session Array</t>
                    </li>
                    <li>
                      <t>Responses:
                      </t>
                      <ul spacing="normal">
                        <li>
                          <t>200 OK:
                          </t>
                          <ul spacing="normal">
                            <li>
                              <t>Contents: Session Array (all sessions in request accepted for configuration).</t>
                            </li>
                          </ul>
                        </li>
                        <li>
                          <t>300:
                          </t>
                          <ul spacing="normal">
                            <li>
                              <t>Contents: Modified Session Array, with rejected or additional sessions.</t>
                            </li>
                          </ul>
                        </li>
                        <li>
                          <t>400:
                          </t>
                          <ul spacing="normal">
                            <li>
                              <t>Error</t>
                            </li>
                          </ul>
                        </li>
                      </ul>
                    </li>
                  </ul>
                </li>
              </ul>
            </li>
            <li>
              <t>REMOVE IX PEER
              </t>
              <ul spacing="normal">
                <li>
                  <t>Given a list of Session Arrays, remove the sessions in that list.</t>
                </li>
                <li>
                  <t>This API serves as a notification to the server, as the client may remove the sessions before sending the request to the server.</t>
                </li>
                <li>
                  <t>Server replies back with request ID and deletion status (complete, in-progress).</t>
                </li>
              </ul>
            </li>
          </ul>
        </section>
        <section anchor="utility-api-calls">
          <name>UTILITY API CALLS</name>
          <t>Endpoints which provide useful information for potential interconnections.</t>
          <ul spacing="normal">
            <li>
              <t>/list_locations: LIST POTENTIAL PEERING LOCATIONS
              </t>
              <ul spacing="normal">
                <li>
                  <t>List potential peering locations, both public and private.</t>
                </li>
                <li>
                  <t>Below is based on OpenAPI specification: https://github.com/bgp/autopeer/blob/main/api/openapi.yaml</t>
                </li>
                <li>
                  <t>GET: /list_locations
                  </t>
                  <ul spacing="normal">
                    <li>
                      <t>Request parameters:
                      </t>
                      <ul spacing="normal">
                        <li>
                          <t>asn (Server ASN, with which to list potential connections)</t>
                        </li>
                        <li>
                          <t>location_type (Optional: Peering Location)</t>
                        </li>
                      </ul>
                    </li>
                    <li>
                      <t>Response:
                      </t>
                      <ul spacing="normal">
                        <li>
                          <t>200: OK
                          </t>
                          <ul spacing="normal">
                            <li>
                              <t>Contents: List of Peering Locations.</t>
                            </li>
                          </ul>
                        </li>
                        <li>
                          <t>400:
                          </t>
                          <ul spacing="normal">
                            <li>
                              <t>Error</t>
                            </li>
                          </ul>
                        </li>
                      </ul>
                    </li>
                  </ul>
                </li>
              </ul>
            </li>
            <li>
              <t>/get_status: QUERY FOR REQUEST STATUS
              </t>
              <ul spacing="normal">
                <li>
                  <t>Given a request ID, query for the status of that request.</t>
                </li>
                <li>
                  <t>Given an ASN without request ID, query for status of all connections between client and server.</t>
                </li>
                <li>
                  <t>Below is based on OpenAPI specification: <eref target="https://github.com/bgp/autopeer/blob/main/api/openapi.yaml">https://github.com/bgp/autopeer/blob/main/api/openapi.yaml</eref></t>
                </li>
                <li>
                  <t>GET: /get_status
                  </t>
                  <ul spacing="normal">
                    <li>
                      <t>Request parameters:
                      </t>
                      <ul spacing="normal">
                        <li>
                          <t>asn (requesting client's asn)</t>
                        </li>
                        <li>
                          <t>request_id (optional, UUID of request)</t>
                        </li>
                      </ul>
                    </li>
                    <li>
                      <t>Response:
                      </t>
                      <ul spacing="normal">
                        <li>
                          <t>200: OK
                          </t>
                          <ul spacing="normal">
                            <li>
                              <t>Contents: Session Array of sessions in request_id, if provided.  Else, all existing and in-progress sessions between client ASN and server.</t>
                            </li>
                          </ul>
                        </li>
                        <li>
                          <t>400:
                          </t>
                          <ul spacing="normal">
                            <li>
                              <t>Error (example: request_id is invalid)</t>
                            </li>
                          </ul>
                        </li>
                      </ul>
                    </li>
                  </ul>
                </li>
              </ul>
            </li>
          </ul>
        </section>
        <section anchor="private-peering-draft">
          <name>Private Peering (DRAFT)</name>
          <ul spacing="normal">
            <li>
              <t>ADD/AUGMENT PNI</t>
            </li>
            <li>
              <t>Parameters:
              </t>
              <ul spacing="normal">
                <li>
                  <t>Peer ASN</t>
                </li>
                <li>
                  <t>Facility</t>
                </li>
                <li>
                  <t>email address (contact)</t>
                </li>
                <li>
                  <t>Action type: add/augment</t>
                </li>
                <li>
                  <t>LAG struct:
                  </t>
                  <ul spacing="normal">
                    <li>
                      <t>IPv4</t>
                    </li>
                    <li>
                      <t>IPv6</t>
                    </li>
                    <li>
                      <t>Circuit ID</t>
                    </li>
                  </ul>
                </li>
                <li>
                  <t>Who provides LOA?  (and where to provide it).</t>
                </li>
              </ul>
            </li>
            <li>
              <t>Response:
              </t>
              <ul spacing="normal">
                <li>
                  <t>200:
                  </t>
                  <ul spacing="normal">
                    <li>
                      <t>LAG struct, with server data populated</t>
                    </li>
                    <li>
                      <t>LOA or way to receive it</t>
                    </li>
                    <li>
                      <t>Request ID</t>
                    </li>
                  </ul>
                </li>
                <li>
                  <t>300:
                  </t>
                  <ul spacing="normal">
                    <li>
                      <t>Proposed Modification: LAG struct, LOA, email address for further discussion</t>
                    </li>
                  </ul>
                </li>
                <li>
                  <t>40x: rejections</t>
                </li>
              </ul>
            </li>
            <li>
              <t>REMOVE PNI
              </t>
              <ul spacing="normal">
                <li>
                  <t>As ADD/AUGMENT in parameters.  Responses will include a requestID and status.</t>
                </li>
              </ul>
            </li>
          </ul>
        </section>
      </section>
    </section>
    <section anchor="public-peering-session-negotiation">
      <name>Public Peering Session Negotiation</name>
      <t>As part of public peering configuration, this draft must consider how the client and server should handshake at which sessions to configure peering.
At first, a client will request sessions A, B, and C.
The server may choose to accept all sessions A, B, and C.
At this point, configuration proceeds as normal.
However, the server may choose to reject session B.
At that point, the server will reply back with A and C marked as "Accepted," and B as "Rejected."
The server will then configure A and C, and wait for the client to configure A and C.
If the client configured B as well, it will not come up.</t>
      <t>This draft encourages peers to set up garbage collection for unconfigured or down peering sessions, to remove stale configuration and maintain good router hygiene.</t>
      <t>Related to rejection, if the server would like to configure additional sessions with the client, the server may reply back with additional peering sessions D and E.
The server will configure D and E on their side, and D and E will become part of the sessions requested in the UUID.
The client may choose whether or not to accept those additional sessions.
If they do, the client should configure D and E as well.
If they do not, the client will not configure D and E, and the server should garbage-collect those pending sessions.</t>
      <t>As part of the IETF discussion, the authors would like to discuss how to coordinate which side unfilters first.
Perhaps this information could be conveyed over a preferences vector.</t>
    </section>
    <section anchor="private-peering">
      <name>Private Peering</name>
      <t>Through future discussion with the IETF, the specification for private peering will be solidified.
Of interest for discussion includes Letter of Authorization (LOA) negotiation, and how to coordinate unfiltering and configuration checks.</t>
    </section>
    <section anchor="maintenance">
      <name>Maintenance</name>
      <t>This draft does not want to invent a new ticketing system.
However, there is an opportunity in this API to provide maintenance notifications to peering partners.
If there is interest, this draft would extend to propose a maintenance endpoint, where the server could broadcast upcoming and current maintenance windows.</t>
      <t>A maintenance message would follow a format like:</t>
      <ul spacing="normal">
        <li>
          <t>Title: string</t>
        </li>
        <li>
          <t>Start Date: date maintenance start(s/ed): UTC</t>
        </li>
        <li>
          <t>End Date: date maintenance ends: UTC</t>
        </li>
        <li>
          <t>Area: string or enum</t>
        </li>
        <li>
          <t>Details: freeform string</t>
        </li>
      </ul>
      <t>The "Area" field could be a freeform string, or could be a parseable ENUM, like (BGP, PublicPeering, PrivatePeering, Configuration, Caching, DNS, etc).</t>
      <t>Past maintenances will not be advertised.</t>
    </section>
    <section anchor="possible-extensions">
      <name>Possible Extensions</name>
      <t>The authors acknowledge that route-server configuration may also be of interest for this proposed API, and look forward to future discussions in this area.</t>
    </section>
    <section anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</t>
    </section>
  </middle>
  <back>
    <?line 351?>

<section anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>This project is joint work between Meta, AWS, Cloudflare, FullCtl, and Google.
Many thanks to my collaborators: Carlos Aguado (AWS), Ben Blaustein (Meta), Matt Griswold (FullCtl), Ben Ryall (Meta), Arturo Servin (Google), and Tom Strickx (Cloudflare).</t>
    </section>
  </back>
  <!-- ##markdown-source:
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
RucKrqZDBHsTyoVjpkvfsJAfnMc5js5uguKlDuNnrqtSAAqHCGzbMU9hnPXa
jxlngugjPKMk2Rus3OlWJFYgXdNGoS8xR05DQ5Ff5QcnXDm/nOX99dV8eX07
v7qI3iL8+ByGs2I+zUOciXq25yiVdiF4HE1p9AEQMKOCWiAJCBoyVL2L2VIt
ltPl3aIX5kIte4dhj0D36p/VvMo2GSmBG9JfHRhscD2N+wMc9+mJ+5tHjEOF
ZEuypwrixBE8tbsM+apTcmq+Y2oB3oSjxpNw7iTEcSdPAriTALmdtJCNPz3C
ZScWkAlkcZDrxGGtk9+AsU5+I7Y6+ceYKlRgtnyo774sUtNy1sTwOi3jh+2T
IPBa31sr+Bl4Hcr1sY9oxFDpOhlY73P9/uZyxqYD37Ei3TWcwUDVqbwUmLC/
atMtcUM5pO8I4PodIHut2Re5ezuVlliSGPPEDT4d35YChXDkNAHi6F7BWm/b
b4P8wJ4h8DN0RkmHUrGJnbs4DgJ0G8ClYEfU9u6KgLjeI0tHgk9Z0axI2U5l
7iKsjZhoFgOoEUO4qYBqlzaJoJnazrT3qmRJ7f2Jq+L4vqu2J6iNLrIYvBC5
LSmi7n2SIS0xwFYx7+vTyc71styuvHn5Vbcrb166y6WET2LztpYggtkbKySw
7cMfrucLKQS9evPia/ai4VSlYz52eNYyYd38/POoloyCaCaO2cYKW6KNV8jn
CefXNe9l3GxSwrDZRaoCPkkwtl+lCZtlcBiJa/gu967Nrch3AVnhqslNkeGx
uoBrUdRCg0PZYhC+YLHv4r3UJ7zduBUpPlBrQNWWa0EDawGvZ+8NA6wRXjLa
gjcFO5e3d2YHSiTS4yaFttxBcZ4LxP4qz4SuXMqRjWH+6d1KM7qglCXPu9f9
HltRkCW7DzhgC5TOZjvUsBQwwHbIxLCgh87uYe8HSK/IAkngCJ+FLaQA7VQs
UcP2Z4s7neORk/Z32ZRsKLk5J9Xw1sh+nS5PYWp9qobhfOWKUjRyKva2yKf3
5E2lyodJVJPjBqqjvoPSbxvWAIWoTrsjN2Son359bZAaOem+HP+OD/Eub83t
6+dyDAru/7lBlJs9D44eqxdH6aezU1K6G9vh5Z6R8N4HRVWCXxyZOlenTsTu
tiQr7rOanHaex6vSXetwr6ebZLGZvRwjobnrUIpu59PlVC3/cjNb0O2oaw2B
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
0clZQ11KAAA=

-->

</rfc>
