<?xml version="1.0" encoding="utf-8"?>
<?xml-model href="rfc7991bis.rnc"?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     category="std"
     docName="draft-flores-airp-provenance-00"
     ipr="trust200902"
     submissionType="IETF"
     consensus="true"
     obsoletes=""
     updates=""
     xml:lang="en"
     tocInclude="true"
     tocDepth="3"
     symRefs="true"
     sortRefs="true"
     version="3">

  <front>
    <title abbrev="AIRP Provenance Seal and Register">The AIRP Provenance Seal and Serving Register</title>
    <seriesInfo name="Internet-Draft" value="draft-flores-airp-provenance-00"/>

    <author fullname="Justin Philip Flores" initials="J. P." surname="Flores">
      <address>
        <email>justinflores@pm.me</email>
      </address>
    </author>

    <date year="2026" month="August" day="7"/>

    <area>Security</area>
    <workgroup>Network Working Group</workgroup>

    <keyword>provenance</keyword>
    <keyword>inference</keyword>
    <keyword>attribution</keyword>
    <keyword>signature</keyword>

    <abstract>
      <t>
        A response served by an inference provider carries no verifiable statement of what
        produced it. A recipient cannot determine which model generated a given output, nor
        whether the endpoint that served it was authorized by the party whose name is on it.
        Attribution today rests on the serving party's own account of events, offered after
        the fact and at its own discretion.
      </t>
      <t>
        This document specifies two mechanisms that together make that determination
        decidable by a recipient. The Provenance Seal is a detached signature by which a
        provider binds a model identifier, its own identity, and a timestamp to the exact
        bytes of a served response. The Serving Register is a signed document listing, for
        each provider, the endpoints authorized to serve its models, the public keys that
        validate its seals, and whether the provider declares that it seals every response.
        A DNS record under the provider's own domain binds that domain to its register entry
        and to its declared sealing policy, so that a suppressed seal is detectable rather
        than merely absent.
      </t>
      <t>
        The design follows electronic mail authentication: the seal is patterned on DKIM,
        the register on SPF, and the declared sealing policy on the published policy record
        of DMARC.
      </t>
    </abstract>
  </front>

  <middle>

    <section anchor="introduction">
      <name>Introduction</name>
      <t>
        Inference responses arrive as unattributed text. A recipient holding a response has
        no mechanism for establishing which model produced it, whether the model named in
        the response metadata is the model that ran, or whether the host that answered was
        one the named provider had authorized to answer on its behalf. Every element of that
        chain is asserted by the serving party and verified by no one.
      </t>
      <t>
        The consequences are ordinary rather than exotic. A router may substitute a cheaper
        model for the one a caller selected and bill for the one selected. Weights obtained
        outside a provider's distribution may be served under that provider's name. A
        response may be altered between the serving host and the recipient. When an incident
        occurs and the parties disagree about what happened, no party holds evidence, and
        the account that prevails is the account of whoever controlled the pipeline.
      </t>
      <t>
        Electronic mail faced the structurally identical problem and solved it with three
        composable pieces: a signature binding a message to a signing domain, a published
        list of hosts authorized to send for a domain, and a published policy stating what a
        receiver should do when the first two are absent or fail. This document adapts that
        pattern.
      </t>
      <t>
        The two mechanisms specified here belong to the Accountable Inference Reputation
        Protocol (AIRP), described in <xref target="AIRP"/>. AIRP is pronounced "AI-Rep".
      </t>
      <t>
        An earlier revision of this document was published as
        <tt>draft-flores-aidp-provenance-00</tt>, under the name Accountable Inference
        Delivery Protocol. That acronym is already carried by
        <xref target="I-D.vandoulas-aidp"/>, which reached its third revision before this
        work was first posted. The name was changed rather than contested. Deployments
        of the earlier name are addressed in <xref target="iana-header"/>.
      </t>

      <section anchor="scope">
        <name>Scope</name>
        <t>
          This document specifies the wire artifacts and the verification procedure: the
          Provenance Seal, the Serving Register, the DNS binding between them, and the
          ordered checks a verifier performs. It does not specify what a recipient does with
          a verification result. Deferring delivery, annotating output, accumulating
          findings over time, and refusing further exchange are policy behaviors outside the
          scope of this document.
        </t>
        <t>
          The mechanisms here are transport-agnostic in principle. <xref target="transport"/>
          defines a binding for HTTP, which is where inference APIs are deployed today.
        </t>
      </section>

      <section anchor="non-goals">
        <name>Non-Goals</name>
        <t>
          A valid Provenance Seal establishes that a party holding a registered private key
          asserted that a named model produced exactly these bytes at approximately this
          time. It establishes nothing else. In particular, a seal is not evidence that the
          named model actually performed the computation. A provider that signs a false
          model identifier produces a cryptographically valid seal carrying a false claim.
        </t>
        <t>
          What the seal changes is the character of that falsehood. An unsigned false claim
          is deniable; a signed one is a durable, attributable artifact held by the party
          who was deceived. This document specifies the mechanism that makes such a claim
          non-repudiable. It offers no mechanism for detecting it, and readers should not
          treat seal validity as a statement about model behavior, output quality, or
          safety.
        </t>
      </section>
    </section>

    <section anchor="conventions">
      <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&nbsp;14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when,
        they appear in all capitals, as shown here.
      </t>

      <dl newline="false" spacing="normal">
        <dt>Provider:</dt>
        <dd>
          The party that performs inference serving and whose identity a seal asserts. A
          provider holds one or more seal signing keys and one register entry.
        </dd>
        <dt>Verifier:</dt>
        <dd>
          The party that receives a response and evaluates its seal. Typically the client
          software acting for the end user.
        </dd>
        <dt>Serving endpoint:</dt>
        <dd>
          A network location, expressed as an absolute URI, at which a provider's models are
          served.
        </dd>
        <dt>Register entry:</dt>
        <dd>
          The record in the Serving Register describing one provider: its authorized serving
          endpoints, its seal public keys, the model identifiers it is registered to serve,
          and its declared sealing policy.
        </dd>
        <dt>Selector:</dt>
        <dd>
          A short label identifying one seal key within a register entry, permitting a
          provider to hold several keys concurrently and to rotate them independently.
        </dd>
        <dt>Registrar:</dt>
        <dd>
          The party that publishes and signs a Serving Register document.
        </dd>
        <dt>Intended provider:</dt>
        <dd>
          The provider a verifier's own configuration names for an exchange, established
          before the response arrives and independently of anything the response carries.
        </dd>
      </dl>
    </section>

    <section anchor="seal">
      <name>The Provenance Seal</name>

      <section anchor="seal-types">
        <name>Seal Types</name>
        <t>
          This document defines two seals. A <em>pre-seal</em> binds the provider's identity,
          the model identifier, the exchange identifier, and the request digest, and carries
          no content. A <em>terminal seal</em> binds the same fields together with the served
          content.
        </t>
        <t>
          A terminal seal is the artifact that carries the guarantee. A pre-seal exists so
          that a verifier can perform the checks that do not depend on content before any of
          the response is rendered: entry selection, entry status, key resolution, model
          authorization, and freshness. Every failure class in that list is decidable at
          admission time, and moving them there is the difference between refusing a
          response and withdrawing one already shown.
        </t>
        <t>
          Which seals a response carries is determined by its transport, as specified in
          <xref target="transport"/>. A non-streamed response carries a terminal seal,
          because its content is complete when its header fields are emitted. A streamed
          response carries a pre-seal in its header fields and a terminal seal in its final
          event, because a signature over complete content cannot exist before the content
          does.
        </t>
      </section>

      <section anchor="seal-fields">
        <name>Fields</name>
        <t>
          A seal of either type consists of the following fields. All are
          <bcp14>REQUIRED</bcp14>.
        </t>
        <dl newline="false" spacing="normal">
          <dt>register-entry:</dt>
          <dd>The identifier of the provider's register entry.</dd>
          <dt>selector:</dt>
          <dd>The selector identifying the signing key within that entry.</dd>
          <dt>alg:</dt>
          <dd>
            The signature algorithm the signer used. This document defines one value,
            <tt>ed25519</tt>, denoting Ed25519 as specified in <xref target="RFC8032"/>.
            This field is advisory. A verifier does not select its verification routine
            from it; see <xref target="key-resolution"/>.
          </dd>
          <dt>model:</dt>
          <dd>The model identifier the provider asserts produced the response.</dd>
          <dt>provider:</dt>
          <dd>The provider identity as it appears in the register entry.</dd>
          <dt>exchange-id:</dt>
          <dd>
            The identifier of the exchange this response answers, as specified in
            <xref target="exchange-id"/>.
          </dd>
          <dt>request-digest:</dt>
          <dd>
            A digest of the request this response answers, as specified in
            <xref target="request-digest"/>.
          </dd>
          <dt>signed-at:</dt>
          <dd>The time of signing, as an Internet date/time string <xref target="RFC3339"/>.</dd>
          <dt>signature:</dt>
          <dd>
            The signature over the canonical payload of <xref target="canonical"/>, encoded
            in base64url without padding <xref target="RFC4648"/>.
          </dd>
        </dl>
        <t>
          Model identity travels as signed plaintext in the <tt>model</tt> field and is
          verified by checking the signature against a public key obtained from the register
          entry. It is not derived from, encoded within, or extracted from the signature
          value itself.
        </t>
        <t>
          A provider <bcp14>SHOULD</bcp14> name a model at the granularity at which it
          holds the weights fixed, using a fully qualified snapshot identifier rather than
          an alias it re-points over time. A coarse identifier weakens the guarantee in
          proportion to how much it covers: a seal naming an identifier under which the
          provider serves several variants establishes only that one of them answered.
          <xref target="register-entry"/> states the corresponding requirement on register
          entries.
        </t>
      </section>

      <section anchor="exchange-id">
        <name>The Exchange Identifier</name>
        <t>
          The <tt>exchange-id</tt> field binds a seal to one exchange. It is supplied by
          the requesting party on the request, as specified in
          <xref target="binding-request"/>, and echoed by the provider into every seal
          covering the response.
        </t>
        <t>
          A requesting party that intends to verify <bcp14>MUST</bcp14> generate the value
          from a cryptographically secure random source, <bcp14>MUST NOT</bcp14> reuse a
          value across exchanges, and <bcp14>MUST</bcp14> retain it for the duration of the
          exchange. A provider <bcp14>MUST</bcp14> copy the received value verbatim into
          the seals it emits. A verifier <bcp14>MUST</bcp14> treat a seal whose
          <tt>exchange-id</tt> does not equal the value it supplied as invalid.
        </t>
        <t>
          Client supply rather than provider generation is what makes the field useful. A
          provider-generated identifier distinguishes one exchange from another but is
          drawn from a space the provider controls, so a captured response replayed to the
          same client carries an identifier the client cannot recognize as belonging to a
          different exchange. A value the client generated and remembers is one the client
          can check. The cost to a provider is echoing a value it already received.
        </t>
        <t>
          The field also binds the two seals of a streamed response to each other. Without
          it, a pre-seal captured from one exchange and a terminal seal captured from
          another both validate, and the pair asserts a response that was never served.
        </t>
      </section>

      <section anchor="request-digest">
        <name>The Request Digest</name>
        <t>
          The <tt>request-digest</tt> field carries a digest computed by the provider over
          the request it received. Its value is the token <tt>sha-256=</tt> followed by the
          SHA-256 digest of the request content, encoded in base64url without padding
          <xref target="RFC4648"/>. Over HTTP the request content is the octets of the
          request body after removal of any content coding and before any parsing; a
          transport binding for another transport states what it is there.
        </t>
        <t>
          A provider <bcp14>MUST</bcp14> compute the digest over the request as received
          and <bcp14>MUST</bcp14> carry the same value in every seal covering the response.
        </t>
        <t>
          Every other field of a seal describes the response. Without this one, a seal
          binds a response to a provider and to an exchange but not to the request that
          provoked it, and an intermediary holding the request in cleartext can substitute
          its own text, forward it under the exchange identifier the client supplied, and
          relay a response the provider sealed honestly. Every check in
          <xref target="verification"/> passes. The client is shown an attributed answer to
          a request it did not make, with the attribution machinery of this document
          vouching for it.
        </t>
        <t>
          Comparison is a reporting obligation rather than grounds for refusal.
          Intermediaries alter requests for reasons that are ordinary and often disclosed:
          system instructions, tool declarations, retrieved context, and safety wrappers
          are all added on the path. <xref target="request-verification"/> specifies the
          finding.
        </t>
      </section>

      <section anchor="field-constraints">
        <name>Field Value Constraints</name>
        <t>
          The canonical payload of <xref target="canonical"/> is a line-oriented format in
          which header fields are separated by LF and the header block is terminated by an
          empty line. A field value containing LF or CR could therefore introduce additional
          apparent header lines within the signed region, producing bytes that two
          conforming implementations parse differently while both compute the same valid
          signature.
        </t>
        <t>
          Accordingly, the values of <tt>register-entry</tt>, <tt>selector</tt>,
          <tt>alg</tt>, <tt>model</tt>, <tt>provider</tt>, <tt>exchange-id</tt>,
          <tt>request-digest</tt>, and <tt>signed-at</tt> <bcp14>MUST NOT</bcp14>
          contain LF (0x0A) or CR (0x0D). A signer <bcp14>MUST</bcp14> reject such a value
          rather than sign it, and a verifier <bcp14>MUST</bcp14> treat a seal carrying such
          a value as invalid without evaluating its signature.
        </t>
        <t>
          The response content is exempt from this constraint. It is the final element of
          the terminal-seal payload and its length in bytes is bound in the header block
          above it, so no content byte sequence can be reinterpreted as a header field. A
          pre-seal payload carries no content and terminates at its header block.
        </t>
      </section>

      <section anchor="canonical">
        <name>Canonical Payload</name>
        <t>
          A signature is computed over one of the two byte sequences below, in which
          <tt>&lt;LF&gt;</tt> denotes a single LF octet (0x0A), field values are substituted
          verbatim, and <tt>content-length</tt> is the length of the sealed content in
          bytes.
        </t>
        <t>The terminal-seal payload is:</t>
        <artwork type="ascii-art"><![CDATA[
airp-seal/v1<LF>
register-entry:<register-entry><LF>
selector:<selector><LF>
alg:<alg><LF>
model:<model><LF>
provider:<provider><LF>
exchange-id:<exchange-id><LF>
request-digest:<request-digest><LF>
signed-at:<signed-at><LF>
content-length:<content-length><LF>
<LF>
<content>
]]></artwork>
        <t>The pre-seal payload is:</t>
        <artwork type="ascii-art"><![CDATA[
airp-preseal/v1<LF>
register-entry:<register-entry><LF>
selector:<selector><LF>
alg:<alg><LF>
model:<model><LF>
provider:<provider><LF>
exchange-id:<exchange-id><LF>
request-digest:<request-digest><LF>
signed-at:<signed-at><LF>
]]></artwork>
        <t>
          Field order is fixed as shown and <bcp14>MUST NOT</bcp14> vary. No whitespace
          surrounds the colon separator. The pre-seal payload ends with the LF that
          terminates its final field and has no empty line and no content. The terminal-seal
          payload has no trailing LF after the content. The header block of either payload
          is encoded as UTF-8; the content of a terminal-seal payload is appended as octets,
          without re-encoding. The two payloads differ only in their version token and in
          the presence of the content block, so a single parser serves both.
        </t>
        <t>
          The version token is the first line and is inside the signed region. A payload
          constructed under a future version cannot be validated as a version 1 payload, and
          a pre-seal payload cannot be validated as a terminal-seal payload. The distinct
          tokens are what keep a metadata-only signature from being presented as a signature
          over content.
        </t>
        <t>
          The <tt>alg</tt> field is inside the signed region for the same reason. An
          algorithm identifier carried alongside a signature rather than within it is
          modifiable by any party on the path. <xref target="sec-alg"/> states why the
          verifier nonetheless takes its verification routine from the key rather than from
          this field.
        </t>
        <t>
          No canonicalization is performed on the content. The signature covers the served
          bytes exactly as served. A verifier <bcp14>MUST NOT</bcp14> normalize whitespace,
          line endings, or Unicode representation before verifying.
        </t>
      </section>

      <section anchor="signing">
        <name>Signing</name>
        <t>
          A provider signs a canonical payload with the private key corresponding to the
          public key published under the stated selector in its register entry. The value of
          <tt>signed-at</tt> <bcp14>MUST</bcp14> be the time at which that signature was
          computed. Where a response carries two seals, each carries its own
          <tt>signed-at</tt>.
        </t>
        <t>
          A provider that declares a sealing policy of <tt>all</tt>
          (<xref target="seal-policy"/>) <bcp14>MUST</bcp14> seal every response it serves,
          with the seals its transport requires under <xref target="transport"/>.
        </t>
      </section>

      <section anchor="transport">
        <name>HTTP Transport Binding</name>
        <t>
          Over HTTP, a seal is carried in the <tt>AIRP-Seal</tt> response header field or in
          a terminating event of a streamed body. Its value in either position is the JSON
          object representation of the seal fields, encoded in base64url without padding.
        </t>
        <t>
          The JSON member names are <tt>registerEntryId</tt>, <tt>selector</tt>,
          <tt>alg</tt>, <tt>model</tt>, <tt>providerIdentity</tt>, <tt>exchangeId</tt>,
          <tt>requestDigest</tt>, <tt>signedAt</tt>, and <tt>signature</tt>, corresponding
          to the fields of <xref target="seal-fields"/>. Member order in the JSON
          representation is not significant; the canonical payload of
          <xref target="canonical"/> is what is signed, and it fixes its own order. A
          representation carrying a duplicate member name <bcp14>MUST</bcp14> be treated as
          invalid without evaluating its signature.
        </t>

        <section anchor="binding-request">
          <name>Request Fields</name>
          <t>
            A requesting party that intends to verify <bcp14>MUST</bcp14> carry its
            exchange identifier in the <tt>AIRP-Exchange-Id</tt> request header field. The
            value is at least 128 bits drawn from a cryptographically secure random source,
            encoded in base64url without padding <xref target="RFC4648"/>. A retried
            request is a new exchange and <bcp14>MUST</bcp14> carry a new value.
          </t>
          <t>
            A provider <bcp14>MUST</bcp14> copy the received value verbatim into the
            <tt>exchange-id</tt> field of every seal covering the response. Where the
            request carried no such field, the provider <bcp14>MUST</bcp14> set
            <tt>exchange-id</tt> to the empty string. A verifier that supplied a value
            <bcp14>MUST</bcp14> treat any other value, the empty string included, as
            invalid.
          </t>
          <t>
            A response sealed under an exchange identifier is specific to one request and
            <bcp14>MUST NOT</bcp14> be stored by a shared cache, since a cache hit
            delivers to one client a seal echoing another client's identifier, which that
            client is required to reject.
          </t>
        </section>

        <section anchor="binding-nonstreamed">
          <name>Non-Streamed Responses</name>
          <t>
            A non-streamed response <bcp14>MUST</bcp14> carry a terminal seal in the
            <tt>AIRP-Seal</tt> header field. The sealed content is the octets of the
            response body after removal of any content coding and before any parsing.
          </t>
          <t>
            Content coding is removed before sealing because the encoded octets are
            frequently unavailable to the party expected to check them. Intermediaries
            re-encode representations in transit, and the runtimes most likely to host a
            verifier decompress before application code observes the body and offer no way
            to recover the encoded form. Sealing the encoded octets would place the signed
            artifact out of reach of most verifiers and would fail against any intermediary
            that recompressed an otherwise untouched response. Removal of a content coding
            is deterministic, so signer and verifier compute the same octets.
          </t>
          <t>
            A seal is carried in the header field only. This document defines no
            body-carried seal, because removing a member from a JSON body and agreeing on
            the remaining octets is a canonicalization, and two conforming implementations
            would disagree about whitespace and separators while both believing they had
            reconstructed the signed payload.
          </t>
        </section>

        <section anchor="binding-streamed">
          <name>Streamed Responses</name>
          <t>
            A streamed response <bcp14>MUST</bcp14> carry a pre-seal in the
            <tt>AIRP-Seal</tt> header field and a terminal seal in a terminating event of
            the body.
          </t>
          <t>
            Where the body is a server-sent event stream <xref target="HTML"/>, the
            terminal seal is carried as an event whose data is a JSON object with the
            single member <tt>airpSeal</tt> whose value is the seal object of this
            section. That event <bcp14>MUST</bcp14> be the
            last event carrying data before the stream's own end-of-stream event, and
            <bcp14>MUST</bcp14> follow every event carrying content:
          </t>
          <artwork type="ascii-art"><![CDATA[
data: {"airpSeal": {"registerEntryId": "...", ...}}

data: [DONE]
]]></artwork>
          <t>
            The sealed content is the concatenation, in the order served, of the octets
            each event of the stream contributes. A content binding specifies, for one API
            shape, which member of which event type contributes octets and in what
            encoding. Every event type a binding does not name contributes zero octets and
            is not otherwise represented in the payload.
          </t>
          <t>
            A provider serving streamed responses <bcp14>MUST</bcp14> publish a content
            binding and <bcp14>MUST</bcp14> name it in the <tt>contentBinding</tt> member
            of its register entry (<xref target="register-entry"/>), so that a verifier
            obtains it without prior knowledge of the provider. A verifier that does not
            hold the binding an entry names <bcp14>MUST</bcp14> report the response as
            unattributed rather than guessing at a delta shape.
            <xref target="sse-binding"/> gives bindings for the event shapes in common
            use.
          </t>
          <t>
            An event carrying content <bcp14>MUST NOT</bcp14> follow the terminal-seal
            event. A
            verifier <bcp14>MUST</bcp14> treat content arriving after the terminal-seal
            event as a refusing finding and <bcp14>MUST NOT</bcp14> release it. The
            requirement is on the verifier and not only on the signer because a verifier
            that stops accumulating at the seal event validates the signature over the
            content that preceded it and then releases content no signature covers, which
            is a working injection against an implementation that believes it verified.
          </t>
          <t>
            A response <bcp14>MUST</bcp14> carry at most one <tt>AIRP-Seal</tt> header
            field and at most one terminal-seal event. A verifier <bcp14>MUST</bcp14> treat
            a response carrying more than one of either as invalid rather than selecting
            among them.
          </t>
          <t>
            The seal is not carried in an HTTP trailer field. Trailers are inconsistently
            exposed to the software most likely to verify, and the terminating event costs
            a verifier nothing it is not already parsing.
          </t>
          <t>
            The integration this requires of a provider is narrow. Streaming continues
            unchanged, the content accumulated for logging and billing is the content that
            gets signed, one signature is computed when generation ends, and one event is
            emitted. Nothing is buffered, no latency is added, and what the client receives
            mid-stream does not change.
          </t>
        </section>

        <section anchor="binding-scope">
          <name>What Is Sealed</name>
          <t>
            A terminal seal covers what was served, as served. Each transport binding
            defines what that means for the transport it covers, and the definitions differ:
            the octets of a body in <xref target="binding-nonstreamed"/>, the concatenated
            deltas of a stream in <xref target="binding-streamed"/>. A provider offering
            both signs a different artifact in each case. This is correct rather than
            inconsistent, because a verifier always knows which transport delivered the
            response it holds.
          </t>
          <t>
            Sealing served bytes rather than text extracted from them binds a seal to the
            response format, so a provider that changes its response envelope invalidates
            seals made under the old one, and every API shape requires its own binding
            statement. That cost is accepted. The alternative delegates extraction to the
            reader, and two conforming verifiers that disagree about whether tool-call
            blocks, reasoning blocks, or refusals are assistant text will compute different
            payloads from identical bytes and reach different verdicts on the same valid
            signature.
          </t>
        </section>
      </section>
    </section>

    <section anchor="register">
      <name>The Serving Register</name>

      <section anchor="register-document">
        <name>Document Structure</name>
        <t>
          A Serving Register document is a JSON object with the following members:
        </t>
        <dl newline="false" spacing="normal">
          <dt>airpRegisterVersion:</dt>
          <dd>The register format version. This document defines <tt>1</tt>.</dd>
          <dt>issuedAt:</dt>
          <dd>The time of issuance, as an Internet date/time string <xref target="RFC3339"/>.</dd>
          <dt>maxAge:</dt>
          <dd>
            The number of seconds after <tt>issuedAt</tt> beyond which this document
            <bcp14>MUST NOT</bcp14> be used, as specified in
            <xref target="register-freshness"/>.
          </dd>
          <dt>registrar:</dt>
          <dd>An object identifying the publishing registrar.</dd>
          <dt>entries:</dt>
          <dd>An array of register entries, as specified in <xref target="register-entry"/>.</dd>
        </dl>
        <t>
          The document is accompanied by a detached signature over its exact bytes,
          computed by the registrar.
        </t>
      </section>

      <section anchor="register-entry">
        <name>Entries</name>
        <t>
          Each entry is a JSON object with the following members:
        </t>
        <dl newline="false" spacing="normal">
          <dt>id:</dt>
          <dd>
            The entry identifier. This is the value a seal carries in its
            <tt>register-entry</tt> field and the value a provider publishes in the
            <tt>e</tt> tag of <xref target="dns-binding"/>. An identifier
            <bcp14>MUST</bcp14> be unique within the document, <bcp14>MUST NOT</bcp14> be
            assigned to a different provider, and <bcp14>MUST NOT</bcp14> be reused after
            the entry it names is revoked. It <bcp14>MUST</bcp14> consist of characters
            drawn from A-Z, a-z, 0-9, hyphen, period, and underscore, so that it carries in
            a DNS TXT record without ambiguity against the tag syntax of
            <xref target="dns-binding"/>.
          </dd>
          <dt>providerIdentity:</dt>
          <dd>The provider's identity as asserted in the <tt>provider</tt> field of its seals.</dd>
          <dt>status:</dt>
          <dd>One of <tt>active</tt>, <tt>probationary</tt>, or <tt>revoked</tt>.</dd>
          <dt>authorizedEndpoints:</dt>
          <dd>
            An array of absolute URIs. A response is authorized only if the endpoint
            contacted matches one of these under <xref target="endpoint-matching"/>.
          </dd>
          <dt>models:</dt>
          <dd>
            An array of model identifiers this provider is registered to serve. A seal
            naming a model outside this array is invalid. Identifiers
            <bcp14>SHOULD</bcp14> be version-pinned: a provider
            <bcp14>SHOULD</bcp14> register one identifier for each set of weights it holds
            fixed, and <bcp14>SHOULD NOT</bcp14> register an alias it re-points to
            different weights over time. Where a provider registers a coarse identifier,
            substitution among the variants that identifier covers passes the check of
            <xref target="model-authorization"/> undetected.
          </dd>
          <dt>keys:</dt>
          <dd>An array of key objects, as specified in <xref target="keys"/>.</dd>
          <dt>contentBinding:</dt>
          <dd>
            The identifier of the content binding this provider serves, as specified in
            <xref target="binding-streamed"/>. <bcp14>REQUIRED</bcp14> of a provider
            serving streamed responses.
          </dd>
          <dt>sealPolicy:</dt>
          <dd>Either <tt>all</tt> or <tt>none</tt>, as specified in <xref target="seal-policy"/>.</dd>
        </dl>
      </section>

      <section anchor="keys">
        <name>Keys and Selectors</name>
        <t>
          Each key object carries a <tt>selector</tt>, a <tt>publicKeyPem</tt> holding the
          public key in PEM-encoded SubjectPublicKeyInfo form <xref target="RFC8410"/>, and
          a <tt>status</tt> of <tt>current</tt>, <tt>rotating</tt>, <tt>retired</tt>, or
          <tt>compromised</tt>. A key carrying either of the last two
          <bcp14>MUST</bcp14> additionally carry <tt>retiredAt</tt>, an Internet date/time
          string <xref target="RFC3339"/> giving the time from which that status applies.
        </t>
        <t>
          Selectors <bcp14>MUST</bcp14> be unique within an entry and
          <bcp14>MUST NOT</bcp14> be reused for a different key. A verifier
          <bcp14>MUST</bcp14> accept keys with status <tt>current</tt> or
          <tt>rotating</tt>. It <bcp14>MUST</bcp14> reject a seal validated under a key
          with status <tt>compromised</tt>, and <bcp14>MUST</bcp14> reject a seal validated
          under a key with status <tt>retired</tt> whose <tt>signed-at</tt> value is at or
          after that key's <tt>retiredAt</tt>.
        </t>
        <t>
          The two statuses differ in whether they reach backwards, and the difference is
          the difference between rotation and theft. A key is <tt>retired</tt> when its
          holder has stopped signing under it in the ordinary course. Seals made before
          that time remain valid, because nothing about the rotation suggests the key was
          ever held by anyone else. A key is <tt>compromised</tt> when the provider cannot
          vouch for who held it, and every seal made under it fails whatever its date,
          because <tt>signed-at</tt> is asserted by whoever holds the key and a party that
          took one can date its output to any time it chooses.
        </t>
        <t>
          A provider rotating a key publishes the new key as <tt>rotating</tt> alongside the
          <tt>current</tt> key, begins signing under the new selector once the updated
          register has propagated, and subsequently marks the old key <tt>retired</tt> with
          a <tt>retiredAt</tt> no earlier than the last seal it made under that key.
          <xref target="sec-evidence"/> states what the distinction is worth to a party
          holding a seal after the fact.
        </t>
      </section>

      <section anchor="seal-policy">
        <name>Declared Sealing Policy</name>
        <t>
          A provider's <tt>sealPolicy</tt> declares whether the absence of a seal is
          meaningful. A value of <tt>none</tt> means the provider does not commit to sealing,
          and an unsealed response is simply unsealed. A value of <tt>all</tt> means the
          provider commits to sealing every response it serves, and an unsealed response
          purporting to come from it is therefore invalid on its face rather than merely
          unattributed.
        </t>
        <t>
          Under <tt>all</tt>, every response carries the seals its transport requires:
          a terminal seal for a non-streamed response, and a pre-seal together with a
          terminal seal for a streamed one. A streamed response carrying a valid pre-seal
          and no terminal seal does not satisfy the commitment;
          <xref target="incomplete"/> specifies how a verifier reports it.
        </t>
        <t>
          This is the mechanism by which stripping a seal becomes detectable. Without it, an
          attacker able to remove the <tt>AIRP-Seal</tt> header field would reduce a sealed
          exchange to an unsealed one and gain the treatment afforded to providers that
          never sealed at all.
        </t>
      </section>

      <section anchor="distribution">
        <name>Distribution and Trust Anchor</name>
        <t>
          A verifier obtains a register document and its detached signature by any means,
          and validates the signature against a registrar public key it holds
          independently. The registrar identification carried inside the document is
          descriptive only. A verifier <bcp14>MUST NOT</bcp14> validate a register document
          against a key obtained from that document.
        </t>
        <t>
          A verifier <bcp14>MUST NOT</bcp14> use a register document whose signature does
          not validate against an independently held registrar key.
        </t>
        <t>
          Because the document is signed and validated independently of how it arrived, the
          transport carries no security burden. A register served over HTTPS, retrieved from
          a transparency log, or shipped with the verifier is subject to the same check.
        </t>
      </section>

      <section anchor="register-freshness">
        <name>Freshness and Revocation Propagation</name>
        <t>
          A register document states its own expiry. A verifier <bcp14>MUST NOT</bcp14> use
          a document whose <tt>issuedAt</tt> plus <tt>maxAge</tt> precedes the present time,
          and <bcp14>MUST NOT</bcp14> use a document that carries no <tt>maxAge</tt>. A
          registrar <bcp14>MUST</bcp14> reissue before expiry. The
          <bcp14>RECOMMENDED</bcp14> value of <tt>maxAge</tt> is 3600 seconds.
        </t>
        <t>
          This bound is what gives revocation a propagation time. The <tt>revoked</tt>
          status of <xref target="register-entry"/> takes effect for a given verifier when
          that verifier next obtains a document, and without a stated maximum age there is
          no time by which that is guaranteed to have happened. A revoked entry in an
          indefinitely cached document is indistinguishable from an active one.
        </t>
        <t>
          Where a verifier holds an expired document and cannot obtain a current one, it
          <bcp14>MUST</bcp14> treat responses evaluated against the expired document as
          unattributed rather than falling back to it. This is the same fail-closed
          disposition <xref target="distribution"/> takes toward an unsigned document, and
          for the same reason: an attacker who can prevent a verifier from refreshing
          should not thereby extend the life of an entry the registrar has withdrawn.
        </t>
        <t>
          This document does not specify a push mechanism for revocation. A registrar
          <bcp14>MAY</bcp14> offer one, and a verifier holding a document
          <bcp14>SHOULD</bcp14> refresh well before expiry rather than at it. Neither
          substitutes for the bound, which is what makes the worst case finite.
        </t>
      </section>

      <section anchor="dns-binding">
        <name>DNS Binding</name>
        <t>
          A provider binds its own domain to its register entry by publishing a TXT record
          at the <tt>_airp</tt> underscored node name <xref target="RFC8552"/> beneath that
          domain. The record value is a sequence of tag-value pairs separated by
          semicolons:
        </t>
        <artwork type="ascii-art"><![CDATA[
_airp.provider.example. IN TXT
    "v=airp1; e=provider.example.entry;
     r=https://register.example.net/airp; p=all;
     k=Ur9dK3xQ7mB2vN5cJ8fH1gL4oR6tY0aE3wS7zX9pQ2M"
]]></artwork>
        <dl newline="false" spacing="normal">
          <dt>v:</dt>
          <dd>Version. <bcp14>MUST</bcp14> be <tt>airp1</tt> and <bcp14>MUST</bcp14> be first.</dd>
          <dt>e:</dt>
          <dd>The provider's register entry identifier.</dd>
          <dt>r:</dt>
          <dd>The URI of the register document that carries the entry.</dd>
          <dt>p:</dt>
          <dd>The declared sealing policy, <tt>all</tt> or <tt>none</tt>.</dd>
          <dt>k:</dt>
          <dd>
            The key set digest of the provider's entry, as specified in
            <xref target="key-set-digest"/>.
          </dd>
        </dl>
        <t>
          A record <bcp14>MUST</bcp14> carry <tt>v</tt> first and
          <bcp14>MUST NOT</bcp14> carry a tag more than once. A verifier
          <bcp14>MUST</bcp14> ignore tags it does not recognize, so that later tags can be
          added without breaking deployed verifiers, and <bcp14>MUST</bcp14> treat a
          duplicated tag, or the presence of more than one record carrying
          <tt>v=airp1</tt>, as a lookup that did not complete. A record split across
          several character strings is the concatenation of those strings, in order, with
          nothing inserted between them.
        </t>
        <t>
          The record is anchored at the provider's identity domain, not at any serving
          endpoint. A provider serving from many hosts, from a content delivery network, or
          from infrastructure it does not administer publishes one record under the domain
          whose name its seals assert.
        </t>
        <t>
          The <tt>e</tt> and <tt>r</tt> tags establish which entry a verifier consults and
          where to obtain it. They do not establish what that entry contains. A party
          holding a registrar signing key can author an entry bearing the identifier the
          provider's zone names, populate it with keys of its own, and satisfy a verifier
          that checked only those two tags: the identifier matches, the document validates
          against the registrar key the verifier holds, and
          <xref target="distribution"/> places no security burden on how the document
          arrived.
        </t>
        <t>
          The <tt>k</tt> tag is what closes that path. Key material is committed to in the
          provider's own zone, so a forged entry requires both the registrar's signing key
          and control of the provider's domain, and those are held by different parties.
          This restores the property mail authentication obtains by publishing the key
          itself: the party whose name is asserted is the party that says which keys are
          its own.
        </t>
        <t>
          Only <tt>https</tt> is permitted in <tt>r</tt>. A verifier that retrieves a
          register document from a <tt>r</tt> value without operator confirmation
          <bcp14>MUST</bcp14> refuse a URI naming any other scheme, and
          <bcp14>SHOULD</bcp14> refuse one resolving to a loopback, link-local, or private
          address, since the tag is content of a zone the verifier's operator does not
          control.
        </t>
        <t>
          Where the DNS record and the register entry both declare a sealing policy and the
          two disagree, a verifier <bcp14>MUST</bcp14> apply the policy <tt>all</tt> if
          either source declares it. Where the DNS lookup does not complete, a verifier
          <bcp14>MUST</bcp14> apply the policy declared in the register entry and
          <bcp14>MUST NOT</bcp14> treat the failed lookup as a declaration of <tt>none</tt>.
        </t>
      </section>

      <section anchor="key-set-digest">
        <name>Key Set Digest</name>
        <t>
          The key set digest of an entry is the SHA-256 digest of the concatenation, taken
          over every key object of the entry's <tt>keys</tt> array in ascending byte order
          of selector, of the selector, an LF octet, the base64 encoding of the key's DER
          SubjectPublicKeyInfo, and a second LF octet. That encoding is the body of the
          <tt>publicKeyPem</tt> member with its armor lines and all whitespace removed. The
          digest is encoded in base64url without padding <xref target="RFC4648"/>.
        </t>
        <t>
          Key status is outside the digest. A provider commits to which keys are its own,
          and the registrar retains authority over whether a given key is current,
          rotating, retired, or compromised, so that a compromise can be acted on without
          waiting for a zone to change and so that ordinary status changes do not require
          the provider to touch DNS. <xref target="sec-registrar"/> states what a
          compromised registrar can still do within that division.
        </t>
        <t>
          Where a <tt>k</tt> tag is available, a verifier <bcp14>MUST</bcp14> compute the
          key set digest of the selected entry and <bcp14>MUST</bcp14> treat the response
          as unattributed where the two differ. This is a refusing finding.
        </t>
        <t>
          Where no <tt>k</tt> tag is available, whether because the provider publishes none
          or because the lookup did not complete, a verifier <bcp14>MUST NOT</bcp14> treat
          the entry as confirmed. Verifiers <bcp14>SHOULD</bcp14> cache observed key set
          digests and <bcp14>SHOULD</bcp14> treat the disappearance of a previously
          observed <tt>k</tt> tag as suspicious rather than as a routine update, for the
          reason given in <xref target="sec-downgrade"/>.
        </t>
      </section>
    </section>

    <section anchor="relationships">
      <name>Relationship to Existing Signature Work</name>
      <t>
        Both artifacts are now in view, and two bodies of existing work bear on them
        closely enough to be worth addressing before the verification procedure that turns
        on them. The first is where this design comes from. The second is what a reader
        who has signed HTTP messages before will ask about first.
      </t>

      <section anchor="ancestry">
        <name>Electronic Mail Authentication</name>
        <t>
          The correspondence to mail authentication is close enough to be worth stating
          plainly, both to credit the design and to help implementers reason by analogy.
        </t>
        <t>
          The Provenance Seal corresponds to DKIM <xref target="RFC6376"/>: a detached
          signature over a message, validated with a key published by the signing party under
          a selector. This document departs from DKIM deliberately in two places.
        </t>
        <t>
          The first is entry selection. DKIM takes the signing domain from the signature
          header itself, which is sound in mail because the result feeds an alignment check
          against a separately obtained identity. This document has no equivalent later
          alignment step, so <xref target="entry-selection"/> forbids the seal from selecting
          its own validating entry rather than permitting it and correcting for it afterward.
        </t>
        <t>
          The second is algorithm selection. DKIM signs its algorithm tag as part of the tag
          list and takes the algorithm from it. This document signs <tt>alg</tt> as part of
          the canonical payload but takes the algorithm from the key, for the reasons given
          in <xref target="sec-alg"/>. Signing the field is necessary and not sufficient: it
          prevents an attacker from altering the declaration a legitimate signer made, and it
          does nothing about a payload the attacker signed entirely, which is the case that
          matters once more than one algorithm is registered.
        </t>
        <t>
          The Serving Register corresponds to SPF <xref target="RFC7208"/>: a published
          statement of which hosts may act for a named party. It is carried as a signed
          document rather than in DNS because the data is larger, changes more often, and
          benefits from audit history, while DNS retains the jobs it does well: proof of
          domain control and the binding of <xref target="dns-binding"/>.
        </t>
        <t>
          The declared sealing policy of <xref target="seal-policy"/> corresponds to the
          published policy of DMARC <xref target="RFC7489"/>. Its function is identical: to
          convert the absence of authentication from an ambiguous condition into a declared
          failure.
        </t>
      </section>

      <section anchor="http-message-signatures">
        <name>HTTP Message Signatures</name>
        <t>
          <xref target="RFC9421"/> defines a general mechanism for signing an HTTP message.
          A signer names a list of components, header fields and a few derived values such
          as the status code, and signs a base string constructed from them. The body is
          not a component. A signer covering the body carries its digest in a
          <tt>Content-Digest</tt> field <xref target="RFC9530"/> and names that field in
          the component list, so body integrity is reached indirectly.
        </t>
        <t>
          Several elements of this document have counterparts there. The <tt>keyid</tt>
          parameter identifies a key as <tt>selector</tt> does. The <tt>alg</tt> parameter
          carries an algorithm declaration, and the guidance accompanying it agrees with
          <xref target="sec-alg"/> that a verifier should determine the algorithm from the
          key rather than from the message. The <tt>nonce</tt> parameter is available for
          the purpose <tt>exchange-id</tt> serves. A <tt>Content-Digest</tt> over a request
          expresses what <xref target="request-digest"/> expresses. In each case
          <xref target="RFC9421"/> supplies a slot and leaves its meaning to the
          application, and the requirements of <xref target="entry-selection"/> and
          <xref target="request-verification"/> are what filling those slots looks like.
        </t>
        <t>
          The departure is streamed content, and it is not a matter of preference. A
          terminal seal covers the concatenation of the content deltas of a stream, which
          is not a header field, not the message body as any component names it, and not in
          existence when the header block is emitted. The signature must therefore travel
          after the content it covers, which within HTTP means a trailer, and
          <xref target="binding-streamed"/> gives the reasons a trailer does not reach the
          software expected to verify. What remains is a signature carried inside the event
          stream, over a payload assembled from the stream rather than from the HTTP
          message. That is not a use of <xref target="RFC9421"/> but a construction wearing
          its shape, and calling it one would mislead an implementer about what a verifier
          is entitled to assume.
        </t>
        <t>
          A non-streamed response is a different matter. It could be expressed as a
          component list covering <tt>Content-Digest</tt> and a field carrying the
          provenance metadata, and doing so would remove the canonical payload of
          <xref target="canonical"/> from this document along with the field constraints
          that attend it. This document does not take that path because it would leave
          two signing constructions in one protocol, one for responses that stream and one
          for responses that do not, verified by different code and failing in different
          ways. The saving is real and the cost is a seam through the middle of the only
          thing the protocol produces.
        </t>
        <t>
          The remaining difference is one of subject rather than mechanism. What is novel
          here is not how a signature is computed but what a provider is made to assert
          while computing it: which model produced a response, under which registered
          identity, against which entry, answering which request.
          <xref target="RFC9421"/> is silent on all of it, as it should be, and an
          implementation built on it would still need every field of
          <xref target="seal-fields"/> and every check of <xref target="verification"/>
          defined exactly as they are here.
        </t>
      </section>
    </section>

    <section anchor="verification">
      <name>Verification</name>
      <t>
        A verifier evaluates a response against the register entry of the provider it
        intended to contact. The checks below are performed in order. Each check that fails
        produces a finding; findings marked as refusing mean the response
        <bcp14>MUST NOT</bcp14> be treated as attributed to the named provider.
      </t>

      <section anchor="ordering">
        <name>When Checks Run</name>
        <t>
          A response carrying a pre-seal is evaluated twice. The checks that do not depend
          on content run at admission, against the pre-seal, before any of the response is
          rendered: entry selection (<xref target="entry-selection"/>), entry status
          (<xref target="entry-status"/>), seal presence (<xref target="seal-presence"/>),
          freshness (<xref target="freshness"/>), key resolution and signature
          (<xref target="key-resolution"/>), provider identity
          (<xref target="provider-identity"/>), model authorization
          (<xref target="model-authorization"/>), endpoint authorization
          (<xref target="endpoint-matching"/>), and request digest
          (<xref target="request-verification"/>). A refusing finding at admission
          <bcp14>MUST</bcp14> be produced before the first content token is rendered.
        </t>
        <t>
          The same checks run again at completion against the terminal seal, which
          additionally binds the content. A response carrying only a terminal seal is
          evaluated once, at completion.
        </t>
        <t>
          The division is what a pre-seal is for. Wrong provider, unregistered model,
          retired key, revoked entry, unauthorized endpoint, stale timestamp, and missing
          register entry are all decidable before a token is shown. What remains for the
          terminal seal is tampering with content, which is the case that warrants
          interrupting a response already in progress.
        </t>
      </section>

      <section anchor="incomplete">
        <name>Incomplete Exchanges</name>
        <t>
          Where a streamed response carries a valid pre-seal and the stream ends without a
          terminal seal, the verifier <bcp14>MUST</bcp14> report an incomplete exchange.
          This is a refusing finding, and it <bcp14>MUST</bcp14> be distinct from
          unattributed.
        </t>
        <t>
          The two mean different things. An unattributed response is one whose claimed
          origin failed to verify. An incomplete exchange is one whose origin verified and
          whose content did not arrive under signature, which is the shape of a connection
          that dropped as much as it is the shape of an attack. A verifier that collapses
          them tells a user their provider is not who it claims to be when the truth is
          that the response ended early.
        </t>
        <t>
          The pre-seal and terminal seal of one response <bcp14>MUST</bcp14> agree in every
          field they share. A verifier <bcp14>MUST</bcp14> treat disagreement in any of
          <tt>register-entry</tt>, <tt>selector</tt>, <tt>model</tt>, <tt>provider</tt>,
          <tt>exchange-id</tt>, or <tt>request-digest</tt> as a refusing finding.
        </t>
      </section>

      <section anchor="entry-selection">
        <name>Entry Selection</name>
        <t>
          The verifier <bcp14>MUST</bcp14> select the register entry from the intended
          provider. It <bcp14>MUST NOT</bcp14> select the entry using the
          <tt>register-entry</tt> field of the received seal.
        </t>
        <t>
          Where the verifier holds an identity domain for the intended provider, it
          <bcp14>MUST</bcp14> resolve <tt>_airp</tt> beneath that domain
          (<xref target="dns-binding"/>) and take the entry identifier from the
          <tt>e</tt> tag, the register location from the <tt>r</tt> tag, and the key set
          digest from the <tt>k</tt> tag. Where the lookup does not complete, it
          <bcp14>MUST</bcp14> use the entry identifier its own configuration holds for that
          provider. In neither case does an identifier come from the response.
        </t>
        <t>
          Where the selected identifier appears more than once in the register document,
          or appears not at all, the verifier <bcp14>MUST</bcp14> treat the response as
          unattributed rather than choosing among entries or falling back to a match on
          <tt>providerIdentity</tt>.
        </t>
        <t>
          Where a seal is present and its <tt>register-entry</tt> field does not equal the
          identifier of the selected entry, the verifier <bcp14>MUST</bcp14> treat the
          response as unattributed. This is a refusing finding and is not a direction to
          consult the entry the seal named.
        </t>
        <t>
          The requirement exists because a signed object that selects its own validating
          authority proves only self-consistency. A response naming an entry that
          legitimately holds the key that signed it produces a seal that verifies perfectly
          and attests to a provider the user never chose to contact.
        </t>
      </section>

      <section anchor="entry-status">
        <name>Entry Status</name>
        <t>
          Where the selected entry is not present in the register, the response
          <bcp14>MUST</bcp14> be treated as unattributed. Where the entry carries status
          <tt>revoked</tt>, the response <bcp14>MUST</bcp14> be treated as unattributed
          regardless of seal validity.
        </t>
      </section>

      <section anchor="seal-presence">
        <name>Seal Presence</name>
        <t>
          Where no seal is present and the applicable sealing policy is <tt>none</tt>, the
          verifier reports the response as unsealed and continues. This is not a failure.
        </t>
        <t>
          Where no seal is present and the applicable sealing policy is <tt>all</tt>, the
          verifier <bcp14>MUST</bcp14> treat this as a downgrade and the response
          <bcp14>MUST NOT</bcp14> be treated as attributed.
        </t>
        <t>
          Presence is evaluated against the seals the response's transport requires under
          <xref target="transport"/>. A streamed response arriving with no header-carried
          pre-seal is a downgrade at admission. A streamed response whose pre-seal is
          present and whose terminal seal never arrives is an incomplete exchange
          (<xref target="incomplete"/>) rather than a downgrade, because the commitment was
          met at admission and the stream did not finish.
        </t>
      </section>

      <section anchor="freshness">
        <name>Freshness</name>
        <t>
          Where a seal is present, the verifier <bcp14>MUST</bcp14> evaluate its
          <tt>signed-at</tt> value against the receipt time of the response that carried it.
        </t>
        <t>
          Receipt time is the time the first octet of the response was received, for a
          pre-seal and for a terminal seal carried in a header field, and the time the last
          octet was received, for a terminal seal carried in a terminating event. A long
          generation may separate the two by minutes, and evaluating both against a single
          instant would either fail the terminal seal of a slow response or admit a stale
          pre-seal.
        </t>
        <t>
          A seal dated more than a permitted forward skew after its receipt time
          <bcp14>MUST</bcp14> be treated as invalid. Signing necessarily precedes receipt,
          so any forward offset is clock disagreement. The <bcp14>RECOMMENDED</bcp14>
          forward skew allowance is 300 seconds.
        </t>
        <t>
          A seal dated more than a permitted age before its receipt time
          <bcp14>MUST</bcp14> be treated as invalid. The <bcp14>RECOMMENDED</bcp14> maximum
          age is 300 seconds.
        </t>
        <t>
          Freshness is evaluated against the receipt time of the response that carried the
          seal, and not against the verifier's clock at the moment of evaluation. This
          distinction matters where a verifier re-evaluates a stored exchange: verification
          of a retained response yields the same verdict later that it yielded at delivery,
          which would not hold if the comparison were against present time.
        </t>
        <t>
          A seal whose <tt>signed-at</tt> value is not a parseable Internet date/time string
          <bcp14>MUST</bcp14> be treated as invalid.
        </t>
      </section>

      <section anchor="key-resolution">
        <name>Key Resolution and Signature</name>
        <t>
          The verifier locates the key in the selected entry whose selector equals the
          <tt>selector</tt> field of the seal. Where no such key exists, the response
          <bcp14>MUST</bcp14> be treated as unattributed. Where the located key carries
          status <tt>compromised</tt>, or status <tt>retired</tt> with a
          <tt>retiredAt</tt> at or before the seal's <tt>signed-at</tt>, the response
          <bcp14>MUST</bcp14> be treated as unattributed (<xref target="keys"/>).
        </t>
        <t>
          A signature <bcp14>MUST</bcp14> be validated under the strict verification
          criteria of <xref target="RFC8032"/>, rejecting non-canonical encodings. A
          verdict that is intended to hold up later cannot depend on which permissive
          verifier happened to evaluate it.
        </t>
        <t>
          The verifier <bcp14>MUST</bcp14> determine the signature algorithm from the
          located key. The <tt>publicKeyPem</tt> member is SubjectPublicKeyInfo
          <xref target="RFC8410"/>, which carries an algorithm identifier, and that
          identifier selects the verification routine. The verifier
          <bcp14>MUST NOT</bcp14> select a verification routine using the <tt>alg</tt>
          field of the seal.
        </t>
        <t>
          Where the <tt>alg</tt> field of the seal names an algorithm other than the one
          the located key carries, the seal <bcp14>MUST</bcp14> be treated as invalid. The
          field is verified for agreement; it is never obeyed.
        </t>
        <t>
          The verifier reconstructs the canonical payload of <xref target="canonical"/> from
          the seal fields and, for a terminal seal, the received content, and validates the
          signature against the located public key under the algorithm the key names. Where
          the signature does not validate, the response <bcp14>MUST</bcp14> be treated as
          unattributed.
        </t>
      </section>

      <section anchor="provider-identity">
        <name>Provider Identity</name>
        <t>
          Where the signature validates, the verifier <bcp14>MUST</bcp14> check that the
          <tt>provider</tt> field of the seal equals the <tt>providerIdentity</tt> member
          of the selected entry. A seal naming any other provider <bcp14>MUST</bcp14> be
          treated as invalid.
        </t>
        <t>
          The field is what a verifier displays and what an artifact carries into a later
          dispute, and until this check runs nothing constrains it. A provider can
          otherwise sign under a name the register does not associate with it and the seal
          still validates: the signature is good, the entry is the one the verifier
          selected for its own reasons, and only the identity on the face of the response
          is unsupported.
        </t>
      </section>

      <section anchor="model-authorization">
        <name>Model Authorization</name>
        <t>
          Where the signature validates, the verifier <bcp14>MUST</bcp14> check that the
          <tt>model</tt> field of the seal appears in the <tt>models</tt> array of the
          selected entry. A seal naming a model the entry is not registered to serve
          <bcp14>MUST</bcp14> be treated as invalid.
        </t>
      </section>

      <section anchor="endpoint-matching">
        <name>Endpoint Authorization</name>
        <t>
          The verifier <bcp14>MUST</bcp14> check the endpoint actually contacted against the
          <tt>authorizedEndpoints</tt> array of the selected entry. A contacted URI matches
          a registered URI when all of the following hold:
        </t>
        <ul spacing="normal">
          <li>Neither URI carries userinfo.</li>
          <li>
            The two URIs have identical origins: the same scheme, the same host compared
            case-insensitively, and the same port after normalization of the scheme's
            default port.
          </li>
          <li>
            The contacted path, after removal of trailing slash characters, either equals
            the registered path after the same removal, or begins with the registered path
            followed by a slash.
          </li>
        </ul>
        <t>
          Matching <bcp14>MUST NOT</bcp14> be performed by string prefix comparison on the
          URIs. Such a comparison authorizes a contacted URI of
          <tt>https://api.example.com/v1evil</tt> against a registered URI of
          <tt>https://api.example.com/v1</tt>, because the registered string is a prefix of
          the contacted string without being a prefix of its path segments.
        </t>
        <t>
          Where the contacted endpoint does not match and no valid seal is present, the
          response <bcp14>MUST</bcp14> be treated as unattributed.
        </t>
        <t>
          Where the contacted endpoint does not match and a seal over the response validates
          against the selected entry, the verifier <bcp14>MUST</bcp14> report the response
          as relayed rather than unattributed. This is not a refusing finding. The verifier
          <bcp14>MUST</bcp14> report the contacted endpoint alongside the attribution, so
          that the party the response passed through is visible to whoever reads the
          finding.
        </t>
        <t>
          Relay is permitted because forbidding it would make the substitution this document
          exists to detect undetectable. Consider a router that a caller asked for model X
          and that serves cheaper model Y. It has three options. It can relay no seal, which
          trips the downgrade finding of <xref target="seal-presence"/> wherever the policy
          is <tt>all</tt>. It can forge a seal, which it cannot do without the provider's
          private key. Or it can relay Y's honest seal, in which case the <tt>model</tt>
          field reads Y, the caller asked for X, and the substitution is on the face of the
          response. Only the third path is available to an honest intermediary, and only the
          third path exposes a dishonest one. A verifier that refuses relayed seals leaves
          the router with nothing to relay.
        </t>
        <t>
          What endpoint authorization gives up here is less than it appears. A detached
          signature over content survives relay intact, so where a signature validates, the
          endpoint check is confirming something the signature has already established. The
          case it still covers is a party holding the provider's private key and serving
          from an unregistered host, which is key compromise, and against key compromise
          endpoint matching was never the defense.
        </t>
      </section>

      <section anchor="request-verification">
        <name>Request Digest</name>
        <t>
          A verifier that retained the request it sent <bcp14>MUST</bcp14> compute the
          digest of <xref target="request-digest"/> over that request and compare it with
          the <tt>request-digest</tt> field of the seal. Where the two differ, the verifier
          <bcp14>MUST</bcp14> report that the request was modified in transit, and
          <bcp14>MUST</bcp14> report it distinctly from every other finding of this
          section.
        </t>
        <t>
          This is a reported finding and not a refusing one. The response is attributable,
          the provider sealed what it in fact received, and the modification may be one the
          user's own deployment performs deliberately. What the finding establishes is that
          the answer replies to something other than what was asked, which is a fact the
          asking party is entitled to and which no other check here surfaces.
        </t>
      </section>
    </section>

    <section anchor="iana">
      <name>IANA Considerations</name>

      <section anchor="iana-header">
        <name>HTTP Field Name Registration</name>
        <t>
          IANA is requested to register the following entry in the "Hypertext Transfer
          Protocol (HTTP) Field Name Registry" defined in <xref target="RFC9110"/>:
        </t>
        <dl newline="false" spacing="compact">
          <dt>Field Name:</dt>
          <dd>AIRP-Seal</dd>
          <dt>Status:</dt>
          <dd>permanent</dd>
          <dt>Reference:</dt>
          <dd>This document, <xref target="transport"/></dd>
        </dl>
        <t>
          IANA is further requested to register:
        </t>
        <dl newline="false" spacing="compact">
          <dt>Field Name:</dt>
          <dd>AIRP-Exchange-Id</dd>
          <dt>Status:</dt>
          <dd>permanent</dd>
          <dt>Reference:</dt>
          <dd>This document, <xref target="binding-request"/></dd>
        </dl>
        <t>
          Deployments predating this document carry a seal in a field named
          <tt>X-AIDP-Seal</tt> or <tt>AIDP-Seal</tt>, under the protocol's former name.
          Neither is registered. <tt>X-AIDP-Seal</tt> is deprecated in accordance with
          <xref target="RFC6648"/>, and both are superseded by <tt>AIRP-Seal</tt>.
        </t>
        <t>
          A verifier <bcp14>MAY</bcp14> accept either legacy field name for compatibility
          with existing deployments and <bcp14>MUST</bcp14> prefer <tt>AIRP-Seal</tt> where
          more than one is present. A seal carried under a legacy field name signs a
          canonical payload whose version token is <tt>aidp-seal/v1</tt> rather than
          <tt>airp-seal/v1</tt>. A verifier that accepts such a seal
          <bcp14>MUST</bcp14> validate it against that token and
          <bcp14>MUST NOT</bcp14> validate a payload carrying one token as though it
          carried the other.
        </t>
      </section>

      <section anchor="iana-dns">
        <name>Underscored Node Name Registration</name>
        <t>
          IANA is requested to register the following entry in the "Underscored and Globally
          Scoped DNS Node Names" registry defined in <xref target="RFC8552"/>:
        </t>
        <dl newline="false" spacing="compact">
          <dt>RR Type:</dt>
          <dd>TXT</dd>
          <dt>_NODE NAME:</dt>
          <dd>_airp</dd>
          <dt>Reference:</dt>
          <dd>This document, <xref target="dns-binding"/></dd>
        </dl>
      </section>

      <section anchor="iana-alg">
        <name>AIRP Provenance Seal Algorithms Registry</name>
        <t>
          IANA is requested to create a registry titled "AIRP Provenance Seal Algorithms",
          with registration policy Specification Required <xref target="RFC8126"/>. Each
          entry carries an algorithm token, a reference, and a status of either
          <tt>current</tt> or <tt>deprecated</tt>.
        </t>
        <t>The initial contents are:</t>
        <table>
          <thead>
            <tr><th>Token</th><th>Status</th><th>Reference</th></tr>
          </thead>
          <tbody>
            <tr><td>ed25519</td><td>current</td><td>This document, <xref target="RFC8032"/></td></tr>
          </tbody>
        </table>
        <t>
          A registrar <bcp14>MUST NOT</bcp14> publish a key whose SubjectPublicKeyInfo names
          an algorithm absent from this registry or present with status
          <tt>deprecated</tt>, and a verifier <bcp14>MUST</bcp14> treat a seal validated
          under such a key as invalid. The registry constrains the algorithms that may
          appear in register entries. It does not constrain the <tt>alg</tt> token of a
          seal, which under <xref target="key-resolution"/> selects nothing.
        </t>
      </section>
    </section>

    <section anchor="security">
      <name>Security Considerations</name>

      <section anchor="sec-what-is-proven">
        <name>What a Valid Seal Establishes</name>
        <t>
          A valid seal establishes that a holder of a registered private key asserted the
          named model, the named provider identity, and the given time over exactly the
          received bytes. It does not establish that the named model performed the
          computation. <xref target="non-goals"/> states this limit; it bears repeating here
          because the failure mode is a verifier that reports a valid seal in language a
          user reads as a guarantee about what answered them.
        </t>
        <t>
          Implementations <bcp14>SHOULD</bcp14> surface seal validity in terms of
          attribution rather than in terms of correctness, safety, or identity of
          computation.
        </t>
      </section>

      <section anchor="sec-injection">
        <name>Canonical Payload Injection</name>
        <t>
          <xref target="field-constraints"/> forbids LF and CR in header field values. The
          constraint is enforced at signing rather than only at verification so that an
          ambiguous payload cannot be produced at all. A signer that enforces the constraint
          only on verification remains capable of emitting a seal that a divergent parser
          reads differently, which is the condition the constraint exists to prevent.
        </t>
      </section>

      <section anchor="sec-alg">
        <name>Algorithm Selection</name>
        <t>
          <xref target="key-resolution"/> requires that the verification routine be chosen
          from the located key rather than from the <tt>alg</tt> field of the seal. The
          field travels with the signature and is therefore modifiable by any party that can
          modify the signature, which is every party on the path. A verifier that selects
          on it lets an attacker choose the algorithm under which the attacker's own bytes
          are checked.
        </t>
        <t>
          This is the algorithm confusion pattern familiar from signed-token formats that
          place the algorithm identifier in an attacker-modifiable header. Its consequences
          range from denial of service, where a rewritten token names an algorithm the
          verifier does not implement, to forgery, where the named algorithm admits a
          cheaper proof than the one the signer used. This document registers a single
          algorithm today, so only the first is presently reachable. The second becomes
          reachable the moment a second algorithm is registered, and by then the deployed
          verifiers are already written.
        </t>
        <t>
          SubjectPublicKeyInfo <xref target="RFC8410"/> carries an algorithm identifier as
          part of the key. Taking the routine from there costs a verifier nothing and
          removes the choice from the wire entirely.
        </t>
        <t>
          The <tt>alg</tt> field remains in the seal and inside the signed region. It
          declares what the signer used, and disagreement between it and the key is a signal
          worth acting on, which is why <xref target="key-resolution"/> makes that
          disagreement invalidating. It is checked rather than followed.
        </t>
      </section>

      <section anchor="sec-replay">
        <name>Replay</name>
        <t>
          A seal is bound to its content, so a captured seal cannot be attached to different
          content. Replay of a response together with its original seal is addressed by the
          <tt>exchange-id</tt> field of <xref target="exchange-id"/>: a verifier that
          generated the value it supplied recognizes a seal echoing a different one, and a
          replayed response therefore fails against the exchange it is replayed into.
        </t>
        <t>
          This holds only where the requesting party generates the value unpredictably and
          does not reuse it. A verifier that reuses an identifier, or accepts one the
          provider chose, retains the exposure the field exists to remove, and the freshness
          bounds of <xref target="freshness"/> are then all that limits the window.
        </t>
        <t>
          The residual case is replay to a party that is not the verifier holding the
          exchange identifier. A response and its seals remain a valid, attributable
          artifact wherever they are carried, which is a property this document intends: a
          seal is durable evidence, and durable evidence can be shown to anyone. What such a
          third party learns is that the named provider served these bytes in some exchange,
          not that it served them in the exchange the third party is looking at.
        </t>
      </section>

      <section anchor="sec-downgrade">
        <name>Downgrade</name>
        <t>
          The declared sealing policy is the sole defense against seal stripping, and it is
          only as reliable as the verifier's ability to learn it. An attacker positioned to
          strip the <tt>AIRP-Seal</tt> field is frequently also positioned to interfere with
          the verifier's DNS resolution.
        </t>
        <t>
          <xref target="dns-binding"/> therefore requires that a failed DNS lookup fall back
          to the register-declared policy rather than to <tt>none</tt>. Verifiers
          <bcp14>SHOULD</bcp14> additionally cache observed policy declarations and
          <bcp14>SHOULD</bcp14> treat a transition from <tt>all</tt> to <tt>none</tt> for a
          previously observed provider as suspicious rather than as a routine update.
        </t>
        <t>
          The same reasoning covers the <tt>k</tt> tag. An attacker who suppresses DNS
          resolution removes the confirmation of <xref target="key-set-digest"/> and
          returns the verifier to trusting a register document on the registrar signature
          alone, which is why a previously observed <tt>k</tt> tag that stops appearing is
          treated as a signal rather than as an absence. Plaintext DNS bounds what this
          costs: an attacker on the resolution path can deny attribution and can raise
          false alarms, and can forge neither a seal nor an entry.
        </t>
      </section>

      <section anchor="sec-registrar">
        <name>Registrar Compromise</name>
        <t>
          A registrar signing key is a trust anchor. Its compromise would otherwise permit
          forged entries, and forged entries permit attribution of arbitrary responses to
          arbitrary providers. The key set digest of <xref target="key-set-digest"/> is
          what prevents it. An attacker holding a registrar key can author an entry bearing
          a provider's identifier, but cannot make keys of its own match the digest that
          provider publishes in its own zone.
        </t>
        <t>
          What a registrar key alone still reaches is everything the digest does not cover.
          It can mark an active entry <tt>revoked</tt>, denying attribution to a provider
          serving honestly. It can restore a key the provider had retired, which is why
          <xref target="keys"/> makes <tt>compromised</tt> the status a provider reaches
          for whenever a key may be in other hands, since that status is retroactive and
          restoring it does not un-sign anything. It can widen
          <tt>authorizedEndpoints</tt> or <tt>models</tt>, neither of which confers an
          ability to sign. And it can forge entries outright for any provider publishing no
          <tt>k</tt> tag.
        </t>
        <t>
          Verifiers <bcp14>SHOULD</bcp14> distinguish, in whatever they report, between an
          entry confirmed by a key set digest and an entry taken from a register document
          alone.
        </t>
        <t>
          This document does not specify registrar governance, registrar accreditation, or
          the process by which a provider obtains an entry.
        </t>
      </section>

      <section anchor="sec-evidence">
        <name>Durability of Attribution</name>
        <t>
          <xref target="non-goals"/> describes a seal as a durable artifact held by the
          party that was deceived. That durability is bounded by the register, which states
          only its present contents. A verdict reached today against today's register is
          not necessarily the verdict the same seal draws a year from now against the
          register of that day, and the party with an interest in the later verdict is
          frequently the party that publishes the entry.
        </t>
        <t>
          The <tt>retiredAt</tt> of <xref target="keys"/> narrows this. Ordinary rotation
          no longer disturbs seals that predate it, which removes the largest routine cause
          of a verdict changing under a seal that nothing was ever wrong with. What remains
          is that a provider may report a key <tt>compromised</tt>, which invalidates its
          output retroactively and by design, and a third party holding a seal cannot
          distinguish a genuine compromise from a convenient one. A verifier that intends
          to rely on a finding later <bcp14>SHOULD</bcp14> retain the verdict it reached at
          receipt together with the register document it reached it against, rather than
          re-deriving the verdict from a register obtained afterward.
        </t>
        <t>
          Attribution that holds against a party able to rewrite its own register requires
          the register's history to be independently observable. This document does not
          specify that. An append-only transparency log over register documents would
          supply it, and is left to future work.
        </t>
      </section>

      <section anchor="sec-privacy">
        <name>Privacy</name>
        <t>
          A verifier that resolves <tt>_airp</tt> beneath a provider's domain before or
          during an exchange discloses to its resolver, and to any observer of that
          resolution, which inference providers a user is contacting and approximately when.
          The DNS queries are a side channel that the exchange itself, carried over TLS to a
          possibly shared host, may not otherwise expose.
        </t>
        <t>
          Verifiers <bcp14>SHOULD</bcp14> cache DNS bindings for their full TTL rather than
          resolving per exchange, and <bcp14>SHOULD</bcp14> use encrypted DNS transport
          where available.
        </t>
        <t>
          The register document itself carries no user-specific information, and retrieving
          it in full rather than querying for individual entries avoids disclosing which
          entries a verifier is interested in. Verifiers <bcp14>SHOULD</bcp14> retrieve
          whole register documents for this reason.
        </t>
      </section>

      <section anchor="sec-request">
        <name>What the Request Digest Does Not Cover</name>
        <t>
          The digest of <xref target="request-digest"/> establishes that the request the
          provider sealed over is or is not the request the client sent. It establishes
          nothing about what the provider did with it, and it is computed by the provider,
          so it is worth exactly what the rest of the seal is worth: a signed assertion by
          a registered key holder, non-repudiable and unverified.
        </t>
        <t>
          A verifier that did not retain its request cannot perform the comparison, and the
          field is then inert. Verifiers <bcp14>SHOULD</bcp14> retain the digest of each
          request they send for the life of the exchange, which costs no more than the
          exchange identifier already costs and is what makes the check available at all.
        </t>
      </section>

      <section anchor="sec-endpoint">
        <name>Endpoint Authorization Is Not Transport Security</name>
        <t>
          The endpoint check of <xref target="endpoint-matching"/> establishes that the
          contacted URI is one the provider registered. It is not a substitute for
          authenticating the connection. A verifier <bcp14>MUST</bcp14> continue to perform
          normal TLS server authentication; a registered endpoint reached over an
          unauthenticated connection offers no assurance.
        </t>
      </section>
    </section>

  </middle>

  <back>
    <references>
      <name>References</name>

    <references>
      <name>Normative References</name>

      <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119">
        <front>
          <title>Key words for use in RFCs to Indicate Requirement Levels</title>
          <author initials="S." surname="Bradner" fullname="S. Bradner"/>
          <date year="1997" month="March"/>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="2119"/>
        <seriesInfo name="DOI" value="10.17487/RFC2119"/>
      </reference>

      <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
        <front>
          <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
          <author initials="B." surname="Leiba" fullname="B. Leiba"/>
          <date year="2017" month="May"/>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="8174"/>
        <seriesInfo name="DOI" value="10.17487/RFC8174"/>
      </reference>

      <reference anchor="RFC3339" target="https://www.rfc-editor.org/info/rfc3339">
        <front>
          <title>Date and Time on the Internet: Timestamps</title>
          <author initials="G." surname="Klyne" fullname="G. Klyne"/>
          <author initials="C." surname="Newman" fullname="C. Newman"/>
          <date year="2002" month="July"/>
        </front>
        <seriesInfo name="RFC" value="3339"/>
        <seriesInfo name="DOI" value="10.17487/RFC3339"/>
      </reference>

      <reference anchor="RFC4648" target="https://www.rfc-editor.org/info/rfc4648">
        <front>
          <title>The Base16, Base32, and Base64 Data Encodings</title>
          <author initials="S." surname="Josefsson" fullname="S. Josefsson"/>
          <date year="2006" month="October"/>
        </front>
        <seriesInfo name="RFC" value="4648"/>
        <seriesInfo name="DOI" value="10.17487/RFC4648"/>
      </reference>

      <reference anchor="RFC8032" target="https://www.rfc-editor.org/info/rfc8032">
        <front>
          <title>Edwards-Curve Digital Signature Algorithm (EdDSA)</title>
          <author initials="S." surname="Josefsson" fullname="S. Josefsson"/>
          <author initials="I." surname="Liusvaara" fullname="I. Liusvaara"/>
          <date year="2017" month="January"/>
        </front>
        <seriesInfo name="RFC" value="8032"/>
        <seriesInfo name="DOI" value="10.17487/RFC8032"/>
      </reference>

      <reference anchor="RFC8410" target="https://www.rfc-editor.org/info/rfc8410">
        <front>
          <title>Algorithm Identifiers for Ed25519, Ed448, X25519, and X448 for Use in the Internet X.509 Public Key Infrastructure</title>
          <author initials="S." surname="Josefsson" fullname="S. Josefsson"/>
          <author initials="J." surname="Schaad" fullname="J. Schaad"/>
          <date year="2018" month="August"/>
        </front>
        <seriesInfo name="RFC" value="8410"/>
        <seriesInfo name="DOI" value="10.17487/RFC8410"/>
      </reference>

      <reference anchor="RFC8552" target="https://www.rfc-editor.org/info/rfc8552">
        <front>
          <title>Scoped Interpretation of DNS Resource Records through "Underscored" Naming of Attribute Leaves</title>
          <author initials="D." surname="Crocker" fullname="D. Crocker"/>
          <date year="2019" month="March"/>
        </front>
        <seriesInfo name="BCP" value="222"/>
        <seriesInfo name="RFC" value="8552"/>
        <seriesInfo name="DOI" value="10.17487/RFC8552"/>
      </reference>

      <reference anchor="RFC9110" target="https://www.rfc-editor.org/info/rfc9110">
        <front>
          <title>HTTP Semantics</title>
          <author initials="R." surname="Fielding" fullname="R. Fielding" role="editor"/>
          <author initials="M." surname="Nottingham" fullname="M. Nottingham" role="editor"/>
          <author initials="J." surname="Reschke" fullname="J. Reschke" role="editor"/>
          <date year="2022" month="June"/>
        </front>
        <seriesInfo name="STD" value="97"/>
        <seriesInfo name="RFC" value="9110"/>
        <seriesInfo name="DOI" value="10.17487/RFC9110"/>
      </reference>

      <reference anchor="RFC8126" target="https://www.rfc-editor.org/info/rfc8126">
        <front>
          <title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
          <author initials="M." surname="Cotton" fullname="M. Cotton"/>
          <author initials="B." surname="Leiba" fullname="B. Leiba"/>
          <author initials="T." surname="Narten" fullname="T. Narten"/>
          <date year="2017" month="June"/>
        </front>
        <seriesInfo name="BCP" value="26"/>
        <seriesInfo name="RFC" value="8126"/>
        <seriesInfo name="DOI" value="10.17487/RFC8126"/>
      </reference>

      <reference anchor="RFC6648" target="https://www.rfc-editor.org/info/rfc6648">
        <front>
          <title>Deprecating the "X-" Prefix and Similar Constructs in Application Protocols</title>
          <author initials="P." surname="Saint-Andre" fullname="P. Saint-Andre"/>
          <author initials="D." surname="Crocker" fullname="D. Crocker"/>
          <author initials="M." surname="Nottingham" fullname="M. Nottingham"/>
          <date year="2012" month="June"/>
        </front>
        <seriesInfo name="BCP" value="178"/>
        <seriesInfo name="RFC" value="6648"/>
        <seriesInfo name="DOI" value="10.17487/RFC6648"/>
      </reference>
    </references>

    <references>
      <name>Informative References</name>

      <reference anchor="RFC6376" target="https://www.rfc-editor.org/info/rfc6376">
        <front>
          <title>DomainKeys Identified Mail (DKIM) Signatures</title>
          <author initials="D." surname="Crocker" fullname="D. Crocker" role="editor"/>
          <author initials="T." surname="Hansen" fullname="T. Hansen" role="editor"/>
          <author initials="M." surname="Kucherawy" fullname="M. Kucherawy" role="editor"/>
          <date year="2011" month="September"/>
        </front>
        <seriesInfo name="STD" value="76"/>
        <seriesInfo name="RFC" value="6376"/>
        <seriesInfo name="DOI" value="10.17487/RFC6376"/>
      </reference>

      <reference anchor="RFC7208" target="https://www.rfc-editor.org/info/rfc7208">
        <front>
          <title>Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1</title>
          <author initials="S." surname="Kitterman" fullname="S. Kitterman"/>
          <date year="2014" month="April"/>
        </front>
        <seriesInfo name="RFC" value="7208"/>
        <seriesInfo name="DOI" value="10.17487/RFC7208"/>
      </reference>

      <reference anchor="RFC7489" target="https://www.rfc-editor.org/info/rfc7489">
        <front>
          <title>Domain-based Message Authentication, Reporting, and Conformance (DMARC)</title>
          <author initials="M." surname="Kucherawy" fullname="M. Kucherawy" role="editor"/>
          <author initials="E." surname="Zwicky" fullname="E. Zwicky" role="editor"/>
          <date year="2015" month="March"/>
        </front>
        <seriesInfo name="RFC" value="7489"/>
        <seriesInfo name="DOI" value="10.17487/RFC7489"/>
      </reference>

      <reference anchor="I-D.vandoulas-aidp" target="https://datatracker.ietf.org/doc/draft-vandoulas-aidp/03/">
        <front>
          <title>Agent Interaction &amp; Delegation Protocol (AIDP)</title>
          <author initials="I." surname="Vandoulas" fullname="Ioannis Vandoulas">
            <organization>Mechapiens</organization>
          </author>
          <date year="2026" month="July" day="20"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-vandoulas-aidp-03"/>
      </reference>

      <reference anchor="I-D.aip-agent-identity-protocol" target="https://datatracker.ietf.org/doc/draft-aip-agent-identity-protocol/00/">
        <front>
          <title>Agent Identity Protocol: Agentic Authentication and Authorized Policy Enforcement</title>
          <author initials="J." surname="Cao" fullname="James Cao">
            <organization>Montcao</organization>
          </author>
          <author initials="C." surname="Arango Gutierrez" fullname="Carlos Eduardo Arango Gutierrez">
            <organization>NVIDIA</organization>
          </author>
          <date year="2026" month="March" day="16"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-aip-agent-identity-protocol-00"/>
      </reference>

      <reference anchor="RFC9421" target="https://www.rfc-editor.org/info/rfc9421">
        <front>
          <title>HTTP Message Signatures</title>
          <author initials="A." surname="Backman" fullname="A. Backman" role="editor"/>
          <author initials="J." surname="Richer" fullname="J. Richer" role="editor"/>
          <author initials="M." surname="Sporny" fullname="M. Sporny"/>
          <date year="2024" month="February"/>
        </front>
        <seriesInfo name="RFC" value="9421"/>
        <seriesInfo name="DOI" value="10.17487/RFC9421"/>
      </reference>

      <reference anchor="RFC9530" target="https://www.rfc-editor.org/info/rfc9530">
        <front>
          <title>Digest Fields</title>
          <author initials="R." surname="Polli" fullname="R. Polli"/>
          <author initials="L." surname="Pardue" fullname="L. Pardue"/>
          <date year="2024" month="February"/>
        </front>
        <seriesInfo name="RFC" value="9530"/>
        <seriesInfo name="DOI" value="10.17487/RFC9530"/>
      </reference>

      <reference anchor="HTML" target="https://html.spec.whatwg.org/multipage/server-sent-events.html">
        <front>
          <title>HTML Living Standard, Section 9.2, Server-sent events</title>
          <author>
            <organization>WHATWG</organization>
          </author>
          <date year="2026"/>
        </front>
      </reference>

      <reference anchor="RFC7942" target="https://www.rfc-editor.org/info/rfc7942">
        <front>
          <title>Improving Awareness of Running Code: The Implementation Status Section</title>
          <author initials="Y." surname="Sheffer" fullname="Y. Sheffer"/>
          <author initials="A." surname="Farrel" fullname="A. Farrel"/>
          <date year="2016" month="July"/>
        </front>
        <seriesInfo name="BCP" value="205"/>
        <seriesInfo name="RFC" value="7942"/>
        <seriesInfo name="DOI" value="10.17487/RFC7942"/>
      </reference>

      <reference anchor="AIRP" target="https://doi.org/10.5281/zenodo.21610185">
        <front>
          <title>The Accountable Inference Reputation Protocol</title>
          <author initials="J. P." surname="Flores" fullname="Justin Philip Flores"/>
          <date year="2026"/>
        </front>
        <seriesInfo name="DOI" value="10.5281/zenodo.21610185"/>
      </reference>

      <reference anchor="ADVOCATE" target="https://github.com/AIRP-spec/inference-advocate">
        <front>
          <title>Inference Advocate: a reference implementation of the client side of AIRP</title>
          <author initials="J. P." surname="Flores" fullname="Justin Philip Flores"/>
          <date year="2026"/>
        </front>
      </reference>
    </references>
    </references>

    <section anchor="implementation">
      <name>Implementation Status</name>
      <t>
        This section records the implementation status of this document in accordance with
        <xref target="RFC7942"/>.
      </t>
      <t>
        An open-source implementation of the verification procedure of
        <xref target="verification"/> is available at <xref target="ADVOCATE"/> under the
        Apache License 2.0. It implements entry selection including the resolution of
        <xref target="dns-binding"/>, entry status, seal presence and policy, freshness,
        key resolution and key status, signature validation, provider identity, model
        authorization, endpoint authorization, and the request digest comparison, together
        with the key set digest of <xref target="key-set-digest"/> and both content
        bindings of <xref target="sse-binding"/>. It refuses content arriving after a
        terminal-seal event. Each of those requirements is covered by a test. It ships a
        mock signing provider so that the signing half is exercisable without a cooperating
        provider, over both the streamed and the non-streamed transport, and it exercises
        both.
      </t>
      <t>
        Two demonstration provider identity domains publish the record of
        <xref target="dns-binding"/>, each carrying a <tt>k</tt> tag, and the register
        document is served at the <tt>r</tt> location with its detached signature
        alongside. The implementation parses the records, refuses unsafe <tt>r</tt>
        targets, computes the key set digest of <xref target="key-set-digest"/> over the
        selected entry, and compares it with the published tag. Entries that set no
        identity domain are reported as unconfirmed, which is the condition
        <xref target="sec-registrar"/> requires be distinguishable from a confirmed one.
        The implementation does not yet retrieve the register over HTTPS.
      </t>
      <t>
        The digests published for the two demonstration domains were computed
        independently from <xref target="key-set-digest"/> by a second party and agreed
        with the implementation's own values, which is some evidence that the construction
        is specified unambiguously.
      </t>
      <t>
        No part of the implementation signs or verifies a pre-seal. The canonical pre-seal
        payload of <xref target="canonical"/> is built and tested, and the streamed parser
        recognizes and skips a pre-seal event, but the agreement checks of
        <xref target="incomplete"/> are not implemented.
      </t>
      <t>
        The deployed Serving Register described in <xref target="distribution"/> does not
        exist. The implementation loads a signed register document from local storage and
        validates it against a pinned registrar key, which is the same check a retrieved
        document receives. No production register, registrar, or accreditation process is
        operating.
      </t>
      <t>
        No inference provider known to the author currently signs its responses. Every
        response reaching a deployed verifier today is unsealed, which this document treats
        as a reportable condition rather than an error precisely so that verification can be
        deployed before signing exists.
      </t>
      <t>This section is to be removed before publication as an RFC.</t>
    </section>

    <section anchor="sse-binding">
      <name>Content Bindings for Server-Sent Event Deltas</name>
      <t>
        This appendix gives two content bindings, covering the two server-sent event
        <xref target="HTML"/> shapes in widest use among inference APIs at the time of
        writing. They exist so that implementations have something concrete to interoperate
        on before providers publish bindings of their own. Neither is normative. A provider
        serving one of these shapes adopts the corresponding binding by naming its
        identifier in the <tt>contentBinding</tt> member of its register entry.
      </t>
      <t>
        The event framing itself does not vary between them, since it is fixed by
        <xref target="HTML"/>: a run of <tt>data</tt> lines terminated by an empty line,
        with the values of several such lines within one event joined by LF. What varies is
        the shape of the JSON object those lines carry, and that is the whole of what a
        binding specifies.
      </t>
      <t>
        Neither binding covers anything other than assistant text. Tool call arguments,
        reasoning or thinking output, and every other member of a delta contribute no
        octets and fall outside the sealed content, which means a seal made under either
        binding says nothing about them. A provider whose responses carry such members and
        that intends to seal them publishes a further binding under a further identifier
        rather than extending one of these, because a verifier holding one of these
        identifiers will not include them, and two verifiers disagreeing about what a
        binding covers is the failure <xref target="binding-scope"/> exists to prevent.
      </t>
      <t>
        In both bindings, an event that contributes nothing contributes zero octets and
        appears nowhere in the payload, and content is concatenated in the order served
        without regard to any index or block a delta may name.
      </t>

      <section anchor="sse-choices-binding">
        <name>Choices and Delta Shape</name>
        <t>
          Binding identifier: <tt>sse-chat-delta-v1</tt>.
        </t>
        <t>
          This binding covers the shape in which each event carries a JSON object holding
          an array of choices and each choice holds a delta object.
        </t>
        <t>
          An event contributes the octets of the UTF-8 encoding of the string value of the
          <tt>content</tt> member of the <tt>delta</tt> object of the first element of the
          <tt>choices</tt> array of its data object, where each of those members is present
          and the value is a string. Every other event contributes nothing: an event whose
          data is not a JSON object, an event carrying no <tt>choices</tt> array, an event
          whose delta carries no <tt>content</tt> member or carries one that is not a
          string, the terminal-seal event of <xref target="binding-streamed"/>, and the
          end-of-stream event.
        </t>
      </section>

      <section anchor="sse-content-block-binding">
        <name>Typed Event Shape</name>
        <t>
          Binding identifier: <tt>sse-content-block-delta-v1</tt>.
        </t>
        <t>
          This binding covers the shape in which each event's data object carries a
          <tt>type</tt> member naming the kind of event, text arrives on events of type
          <tt>content_block_delta</tt>, and a stream carries further event types marking
          the start and end of the message and of each content block.
        </t>
        <t>
          An event contributes the octets of the UTF-8 encoding of the string value of the
          <tt>text</tt> member of the <tt>delta</tt> object of its data object, where the
          data object's <tt>type</tt> is <tt>content_block_delta</tt>, the delta object's
          own <tt>type</tt> is <tt>text_delta</tt>, and the <tt>text</tt> member is present
          and is a string. Every other event contributes nothing: an event whose data is
          not a JSON object, an event whose data object carries any other <tt>type</tt>,
          an event whose delta carries any other <tt>type</tt>, the terminal-seal event of
          <xref target="binding-streamed"/>, and the end-of-stream event.
        </t>
        <t>
          Selection is on the <tt>type</tt> member of the data object rather than on the
          <tt>event</tt> field of the SSE framing. A stream of this shape ordinarily
          carries both, and the two agree, but only the data object is inside what a seal
          covers, and a binding that read the framing would let an intermediary alter the
          selection without altering a sealed octet.
        </t>
      </section>
    </section>


    <section anchor="related">
      <name>Relationship to Agent Identity Work</name>
      <t>
        <xref target="I-D.aip-agent-identity-protocol"/> addresses an adjacent problem and
        reaches two conclusions this document depends on. Its Section 3.2 observes that
        policies expressed as text in a model's system prompt are not tamper-evident and can
        be bypassed by adversarial input, and that no enforcement point exists at the
        infrastructure layer that acts independently of the model. Its Section 9.2 places
        its enforcement proxy outside the model's trust boundary for that reason.
      </t>
      <t>
        This document takes the same position and applies it in the other direction. That
        work governs what an agent may do with a response; this one establishes what the
        response is and who served it. Both rest on the observation that a property enforced
        by asking a model to observe it is not enforced, and both put the enforcing
        component somewhere the model's output cannot reach.
      </t>
      <t>
        The two compose. An enforcement proxy deciding whether to act on assistant text has
        no way today to establish that the text came from the provider it names, and a
        verifier establishing that has no view of what is subsequently done with it. Nothing
        in either document precludes deploying both.
      </t>
    </section>

    <section anchor="acknowledgments" numbered="false">
      <name>Acknowledgments</name>
      <t>
        The architecture from which these two mechanisms are drawn is described in
        <xref target="AIRP"/>.
      </t>
    </section>
  </back>
</rfc>
