{{#> layout }}
<div class="about">
    <center><h2><a href="/about">About</a> | <a href="/about/news">News</a> | <a href="/about/usage">Usage</a> | <a href="/about/faq">FAQ</a> | <a href="/about/stats">Stats</a> | <a href="/about/privacy">Privacy</a></h2></center>

  <p>
    Hagrid implements both the legacy HKP interface, as well as our
    native interface, VKS.
  </p>

  <h2>Verifying Keyserver (VKS) Interface</h2>

  <p>Hagrid has its own URL scheme to fetch, submit, and verify keys.</p>

  <ul>
    <li>
      <tt>GET /vks/v1/by-fingerprint/&lt;FINGERPRINT&gt;</tt>
      <p>
        Retrieves the key with the given <tt>Fingerprint</tt>.
        The <tt>Fingerprint</tt> may refer to the primary key, or any subkey.
        Hexadecimal digits MUST be uppercase,
        and MUST NOT be prefixed with <code>0x</code>.
        The returned key is ASCII Armored, and has a content-type of <code>application/pgp-keys</code>.
      </p>
    </li>

    <li>
      <tt>GET /vks/v1/by-keyid/&lt;KEY-ID&gt;</tt>
      <p>
        Retrieves the key with the given long <tt>KeyID</tt>.
        The <tt>KeyID</tt> may refer to the primary key, or any subkey.
        Hexadecimal digits MUST be uppercase,
        and MUST NOT be prefixed with <code>0x</code>.
        The returned key is ASCII Armored, and has a content-type of <code>application/pgp-keys</code>.
      </p>
    </li>

    <li>
      <tt>GET /vks/v1/by-email/&lt;URI-ENCODED EMAIL-ADDRESS&gt;</tt>
      <p>
        Retrieves the key with the given <tt>Email Address</tt>.
        Only exact matches are accepted.
        Lookup by email address requires opt-in by the owner of the email address.
        The returned key is ASCII Armored, and has a content-type of <code>application/pgp-keys</code>.
      </p>
    </li>

    <li>
      <tt>POST /vks/v1/upload</tt>
      <p>
        A single key may be submitted using a POST request to <tt>/vks/v1/upload</tt>.
        The body of the request must be <code>application/json</code>.
        The JSON data must contain a single field <code>keytext</code>,
        which must contain the keys to submit.
        The value of <code>keytext</code> can be formatted in standard OpenPGP ASCII Armor, or base64.
      </p>

      <p>
        The returned JSON data
        contains the fields <code>token</code>, <code>key_fpr</code>,
        and <code>status</code>.
        The <code>token</code> field contains an opaque value,
        which can be used to perform <tt>request-verify</tt> requests
        (see below).
        The <code>key_fpr</code> field contains the fingerprint of the uploaded primary key.
        The <code>status</code> token contains a map of email addresses
        contained in the key, with one of the values
        <code>unpublished</code>,
        <code>published</code>,
        <code>revoked</code>, or
        <code>pending</code>,
        indicating the status of this email address.
      </p>

      <div class="example">
        <div>
          Example request:
          <pre>
{
  "keytext": "&lt;ASCII ARMORED KEY&gt;"
}
          </pre>
        </div>
        <div>
          Example response:
          <pre>
{
  "key_fpr": "&lt;FINGERPRINT&gt;",
  "status": {
    "address@example.org": "unpublished"
  },
  "token": "..."
}
          </pre>
        </div>
      </div>
    </li>

    <li>
      <tt>POST /vks/v1/request-verify</tt>
      <p>
        A key that has been uploaded
        can be made discoverable by one or more of its email addresses
        by proving ownership of the address
        via a verification email.
        This endpoint requests verification
        for one or more email addresses.
      </p>
      <p>
        The body of the request must be <code>application/json</code>.
        The JSON data must include the opaque <code>token</code> value
        (obtained via <tt>/vks/v1/upload</tt>)
        and an <code>addresses</code> field,
        which is a list of email addresses (not full User IDs)
        to request verification mails for.
        It can optionally include a <code>locale</code> field,
        which is list of locales,
        ordered by preference,
        which to use for the verification email.
        The reply will be the same as for the <tt>/vks/v1/upload</tt> endpoint,
        with addresses marked as <code>pending</code> where a verification email
        has been sent.
      </p>

      <div class="example">
        <div>
          Example request:
          <pre>
{
  "token": "...",
  "addresses": [
    "address@example.org"
  ],
  "locale": [ "de_CH", "de_DE" ]
}
          </pre>
        </div>
        <div>
          Example response:
          <pre>
{
  "key_fpr": "&lt;FINGERPRINT&gt;",
  "status": {
    "address@example.org": "pending"
  },
  "token": "..."
}
          </pre>
        </div>
      </div>
    </li>
  </ul>

  <h3>Error handling</h3>
  <p>
    If a GET request fails for any reason,
    a suitable HTTP status code will be returned.
    The response will be a plaintext error message.
    If a key is not found,
    the HTTP status code will be <tt>404</tt>.
  </p>
  <p>
    If a POST request fails for any reason,
    a suitable HTTP status code will be returned.
    The response body will be
    a JSON object
    with a single <code>error</code> attribute.
    A POST request may fail
    with a HTTP 503 error
    at any time
    if the server is undergoing
    database maintenance.
    <strong>Clients should handle errors gracefully for POST requests.</strong>
  </p>

  <div class="example">
    <div>
      Example response:
      <pre>
{
  "error": "We are currently undergoing scheduled database maintenance!"
}
      </pre>
    </div>
  </div>

  <h3 id="rate-limiting"><a href="#rate-limiting">Rate Limiting</a></h3>

  <p>
    Requests to the <span class="brand">keys.openpgp.org</span> API are rate
    limited:
  </p>
  <ul>
    <li>
      Requests by fingerprint or key id are limited to five requests per second.
      Excessive requests will fail with <tt>error 429</tt>.
      There is a burst window of 1000.
    </li>
    <li>
      Requests by email address are limited to one request per minute.
      Excessive requests will fail with <tt>error 429</tt>.
      There is a burst window of 50.
    </li>
  </ul>

  <h2>HTTP Keyserver Protocol (HKP) Interface</h2>
  <p>
    Hagrid implements a subset of
    the <a href="https://tools.ietf.org/html/draft-shaw-openpgp-hkp-00">HKP</a>
    protocol so that tools like GnuPG and OpenKeychain can use it
    without modification.
  </p>

  <ul>
    <li>
      <tt>GET /pks/lookup?op=get&amp;options=mr&amp;search=&lt;QUERY&gt;</tt>

      <p>Returns an ASCII Armored key matching the query.  Query may be:</p>

      <ul>
        <li>An exact email address query of the form <code>localpart@example.org</code>.</li>
        <li>
          A hexadecimal representation of a long <tt>KeyID</tt>
          (e.g., <code>069C0C348DD82C19</code>, optionally prefixed by <code>0x</code>).
          This may be a <tt>KeyID</tt> of either a primary key or a subkey.
        </li>
        <li>
          A hexadecimal representation of a <tt>Fingerprint</tt>
          (e.g., <code>8E8C33FA4626337976D97978069C0C348DD82C19</code>, optionally prefixed by <code>0x</code>).
          This may be a <tt>Fingerprint</tt> of either a primary key or a subkey.
        </li>
      </ul>
    </li>

    <li>
      <tt>GET /pks/lookup?op=index&amp;options=mr&amp;search=&lt;QUERY&gt;</tt>
      <p>
        Returns
        a <a href="https://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5.2">machine-readable
        list</a> of keys matching the query.  Query may have the forms
        detailed above.  Hagrid always returns either one or no keys at
        all.
      </p>
    </li>

    <li>
      <tt>POST /pks/add</tt>

      <p>
        Keys may be submitted using a POST request to <tt>/pks/add</tt>,
        the body of the request being
        a <code>application/x-www-form-urlencoded</code> query.
        <code>keytext</code> must be the keys to submit,
        which must be ASCII Armored.
        More than one key may be submitted in one request.
        For verification of email addresses,
        the <tt>/vks/v1/upload</tt> endpoint
        must be used instead.
      </p>
    </li>
  </ul>

  <a href="#rate-limiting">Rate limiting</a> applies as for the VKS interface
  above.

  <h4>Limitations</h4>

  <p>
    By design, Hagrid does not implement the full HKP protocol.  The specific
    limitations are:
  </p>

  <ul>
    <li>No support for <code>op=vindex</code>.</li>
    <li>Only exact matches by email address, fingerprint or long key id are returned.</li>
    <li>All requests return either one or no keys.</li>
    <li>The expiration date field in <code>op=index</code> is left blank (discussion <a target="_blank" href="https://gitlab.com/keys.openpgp.org/hagrid/issues/134">here</a>).</li>
    <li>All parameters and options other than <code>op</code> and <code>search</code> are ignored.</li>
    <li>Output is always machine readable (i.e. <code>options=mr</code> is always assumed).</li>
    <li>Uploads are restricted to 1 MiB.</li>
    <li>All packets that aren't public keys, user IDs or signatures are filtered out.</li>
  </ul>

</div>
{{/layout}}
