<?xml version='1.0' encoding='UTF-8'?>
<rfc version="3" docName="draft-li-kvcache-efficient-storage-00" category="std" consensus="true" submissionType="IETF" ipr="trust200902" tocInclude="true" tocDepth="3" sortRefs="true" symRefs="true" xml:lang="en">

  <front>
    <title abbrev="KV Cache Storage Protocol">Key-Value (KV) Cache Storage Protocol (KVSP): Indexing, Placement, Prefetch, and Migration for Distributed Inference</title>
    <seriesInfo name="Internet-Draft" value="draft-li-kvcache-efficient-storage-00"/>

    <author fullname="Zhiqiang Li" initials="Z." surname="Li">
      <organization>China Mobile</organization>
      <address>
        <postal><city>Beijing</city><code>100053</code>
        <country>China</country></postal>
        <email>lizhiqiangyjy@chinamobile.com</email>
      </address>
    </author>
    <author fullname="Zongpeng Du" initials="Z." surname="Du">
      <organization>China Mobile</organization>
      <address>
        <postal><city>Beijing</city><code>100053</code>
        <country>China</country></postal>
        <email>duzongpeng@chinamobile.com</email>
      </address>
    </author>
    <author fullname="Junjie Wang" initials="J." surname="Wang">
      <organization>Centec</organization>
      <address>
        <postal><city>Shanghai</city><code>201203</code>
        <country>China</country></postal>
        <email>wangjj@centec.com</email>
      </address>
    </author>
    <author fullname="Wei Cheng" initials="W." surname="Cheng">
      <organization>Centec</organization>
      <address>
        <postal><city>Shanghai</city><code>201203</code>
        <country>China</country></postal>
        <email>chengw@centec.com</email>
      </address>
    </author>
    <author fullname="Ying Zhao" initials="Y." surname="Zhao">
      <organization>Centec</organization>
      <address>
        <postal><city>Shanghai</city><code>200000</code>
        <country>China</country></postal>
        <email>zhaoying@centec.com</email>
      </address>
    </author>
    <author fullname="Chunhao Zhao" initials="C." surname="Zhao">
      <organization>SAIA</organization>
      <address>
        <postal><city>Shanghai</city><code>200125</code>
        <country>China</country></postal>
        <email>chunhao.zhao@sh-aia.com</email>
      </address>
    </author>
    <author fullname="Xun Sun" initials="X." surname="Sun">
      <organization>Inesa</organization>
      <address>
        <postal><city>Shanghai</city><code>200030</code>
        <country>China</country></postal>
        <email>sunxun@inesa.com</email>
      </address>
    </author>

    <date year="2026" month="November" day="2"/>
    <area>Applications and Real-Time</area>
    <keyword>KV cache</keyword>
    <keyword>distributed inference</keyword>
    <keyword>prefix caching</keyword>
    <keyword>tiered storage</keyword>
    <keyword>prefetch</keyword>
    <keyword>session migration</keyword>
    <keyword>HTTP</keyword>

    <abstract>
      <t>Large language model inference keeps the attention key-value (KV)
      cache of processed tokens so that they are not recomputed.  When
      inference instances, cache tiers, and schedulers are separate
      components, possibly from different implementers, they need a common
      way to name reusable KV cache blocks, find where copies reside,
      protect copies from eviction while they are being loaded, steer work
      to the instance where loading is cheapest, and move a session to
      another instance after a failure.  This document specifies the KV
      Cache Storage Protocol (KVSP), an HTTP-based protocol that defines a
      compatibility descriptor, keyed block identifiers that bind a block
      to its entire token prefix, a self-describing block container, a
      replica index with layer-granular residency, instance status
      reporting, leased prefetch directives, session checkpoints for
      migration, and a resharding procedure that retains sequence shards in
      place when a parallel group is resized.</t>
    </abstract>
  </front>

  <middle>

    <section anchor="intro">
      <name>Introduction</name>

      <section anchor="problem">
        <name>Problem Statement</name>
        <t>The KV cache of a transformer model holds, for every processed
        token and every layer, the key and value vectors used by attention.
        It lets each decoding step reuse earlier computation, but it imposes
        three costs on serving systems:</t>
        <ul>
          <li>Footprint: the cache grows linearly with context length and
          batch size and quickly exhausts accelerator memory, which limits
          both concurrency and maximum context length.</li>
          <li>Request-bound lifetime: when the cache is discarded at the end
          of each request, requests that share a long system prompt or
          document prefix recompute the same state, which increases the time
          to first token.</li>
          <li>Swap cost: moving cache contents between accelerator memory,
          host memory, and storage is slow when done synchronously and
          stalls computation.</li>
        </ul>
        <t>Serving systems address these costs by keeping caches beyond the
        lifetime of a request, storing them in several tiers, and reusing
        them across requests and instances
        <xref target="PAGEDATTENTION"/> <xref target="MOONCAKE"/>.  These
        systems are currently built as single-vendor stacks.  When the
        scheduler, the inference engines, and the cache tiers come from
        different implementers, or span administrative boundaries within an
        operator, every pair of components needs agreement on the same
        questions: which caches are interchangeable, what a block is called,
        where its copies are, how long a copy will stay, which instance
        should serve the next request, and how to continue a session when
        an instance fails.  <xref target="I-D.li-cats-kv-cache-distribution"/>
        states these needs as requirements.  This document specifies a
        protocol that meets them.</t>
      </section>

      <section anchor="overview">
        <name>Protocol Overview</name>
        <t>KVSP defines:</t>
        <ul>
          <li>a Compatibility Descriptor and its digest, the Compatibility ID,
          which state when two caches can substitute for each other
          (<xref target="compat"/>);</li>
          <li>Block Keys computed as a keyed hash chain over token
          identifiers, so that a key names a block together with its entire
          prefix and cannot be inverted by parties that lack the scope key
          (<xref target="keys"/>);</li>
          <li>a KV Block Container format that supports retrieval of a range
          of layers (<xref target="container"/>);</li>
          <li>an Index Service that records replicas, including which layers
          of a block reside in which storage tier (<xref target="index"/>);</li>
          <li>instance status reports and an instance lifecycle including
          draining and sleeping (<xref target="status"/>);</li>
          <li>prefetch directives with eviction-protecting leases, which let
          a placement controller load caches into an instance before the
          request arrives (<xref target="prefetch"/>);</li>
          <li>session checkpoints that let another instance resume a session
          after a failure (<xref target="migration"/>); and</li>
          <li>a resharding procedure for caches split by token range across a
          parallel group, which keeps shards in place when they stay on the
          same instance (<xref target="reshard"/>).</li>
        </ul>
      </section>

      <section anchor="scope">
        <name>Scope</name>
        <t>KVSP standardizes the information exchanged between components
        and the obligations attached to it.  It does not standardize
        attention kernels, memory allocators, eviction policies, or the
        scheduling policy of the placement controller; it standardizes the
        inputs that such a policy consumes and the directives it issues.
        Inference requests themselves (for example, chat completion APIs)
        are out of scope.  Bulk block transfer uses HTTP by default; other
        transports such as Media over QUIC Transport
        <xref target="I-D.li-kvcache-efficient-transmission"/> or RDMA
        <xref target="I-D.li-rdma-kvcache-transfer-optimization"/> can be used
        where available.</t>
      </section>

      <section anchor="related">
        <name>Relationship to Other Work</name>
        <t>Computing-Aware Traffic Steering (CATS)
        <xref target="I-D.ietf-cats-framework"/> steers requests to service
        instances using computing and network metrics.  The instance status
        and lookup estimates of KVSP can serve as additional inputs to such
        steering, as described in
        <xref target="I-D.li-cats-kv-cache-distribution"/>.  Naming Things
        with Hashes <xref target="RFC6920"/> defines hash-based identifiers;
        KVSP uses a keyed construction instead, because plain hashes of
        short token sequences can be inverted by enumeration
        (<xref target="privacy"/>).</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 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>Token:</dt>
        <dd>An integer identifier produced by the model's tokenizer.</dd>
        <dt>Block:</dt>
        <dd>The KV cache entries of B consecutive tokens, for all layers,
        where B is the block size of the Compatibility Descriptor.  Block i
        covers token positions [i*B, (i+1)*B).</dd>
        <dt>Scope:</dt>
        <dd>A set of principals that are allowed to share caches, such as a
        tenant.  A scope is identified by a Scope ID and holds a secret
        Scope Key.</dd>
        <dt>Holder:</dt>
        <dd>A component that stores blocks and serves them, for example an
        inference instance (for its accelerator and host memory) or a cache
        server.</dd>
        <dt>Tier:</dt>
        <dd>A class of storage at a holder, such as accelerator memory or
        host memory (<xref target="iana-tiers"/>).</dd>
        <dt>Replica:</dt>
        <dd>A copy of a block, or of some of its layers, at a holder.</dd>
        <dt>Index Service (IS):</dt>
        <dd>The component that records replicas and answers lookups.</dd>
        <dt>Placement Controller (PC):</dt>
        <dd>The component that assigns requests to inference instances and
        issues directives to them.  It is often called a global scheduler.</dd>
        <dt>Inference Instance (II):</dt>
        <dd>A model execution engine with its own accelerator memory.</dd>
        <dt>Session:</dt>
        <dd>One generation task (a request and its output tokens) whose state
        may outlive the instance that started it.</dd>
        <dt>Lease:</dt>
        <dd>A holder's commitment not to evict specified replicas until the
        lease expires or is released.</dd>
        <dt>Parallel Group:</dt>
        <dd>A set of instances that jointly hold the cache of one context,
        split by token range (sequence parallelism).</dd>
      </dl>
      <t>JSON <xref target="RFC8259"/> member names in this document are
      case-sensitive.  "hex32" denotes 32 octets encoded as 64 lowercase
      hexadecimal characters; "hex16" denotes 16 octets encoded as 32
      lowercase hexadecimal characters.  Durations are integers in
      milliseconds unless the member name says otherwise.</t>
    </section>

    <section anchor="architecture">
      <name>Architecture</name>
      <figure anchor="fig-arch">
        <name>KVSP Roles</name>
        <artwork type="ascii-art">
                  +----------------------+
   status,        | Placement Controller |   prefetch, resume,
   checkpoints --&gt;| (PC)                 |--&gt;reshard directives
                  +----------+-----------+
                             | lookup
                             v
                  +----------------------+
   publish,   ---&gt;| Index Service (IS)   |
   withdraw       +----------------------+

 +---------------+     +---------------+     +---------------+
 | Inference     |     | Inference     |     | Cache server  |
 | Instance 1    |&lt;---&gt;| Instance 2    |&lt;---&gt;| (holder)      |
 | tiers: device,|     | tiers: device |     | tiers: host,  |
 | host          |     |               |     | storage       |
 +---------------+     +---------------+     +---------------+
        block GET, PUT, and leases between all holders
</artwork>
      </figure>
      <t>Roles can be combined.  A deployment can run the PC and IS as one
      service, and every inference instance is normally also a holder for
      its own tiers.  All roles are reached through HTTPS base URIs
      configured by the operator (<xref target="http"/>).</t>
    </section>

    <section anchor="datamodel">
      <name>Data Model</name>

      <section anchor="compat">
        <name>Compatibility Descriptor and Compatibility ID</name>
        <t>A KV cache produced under one configuration is only valid for
        another computation if the model weights, the tokenizer, and every
        parameter that affects the stored tensors are identical.  The
        Compatibility Descriptor is a JSON object with the following
        members, all <bcp14>REQUIRED</bcp14> unless stated otherwise:</t>
        <dl newline="false">
          <dt>model:</dt><dd>A string naming the model.  It is part of the
          identity, so all parties <bcp14>MUST</bcp14> use the same string for the same
          model.</dd>
          <dt>model_digest:</dt><dd>"sha-256:" followed by the lowercase hex
          SHA-256 <xref target="RFC6234"/> of the model weights, computed over
          the weight files concatenated in ascending byte order of their file
          names.</dd>
          <dt>tokenizer_digest:</dt><dd>"sha-256:" followed by the lowercase hex
          SHA-256 of the tokenizer definition file.</dd>
          <dt>adapters:</dt><dd><bcp14>OPTIONAL</bcp14> array of adapter digests, in the same
          form as model_digest, in the order of application.  The member <bcp14>MUST</bcp14>
          be absent when no adapter is applied; an empty array is not
          allowed.</dd>
          <dt>layers, kv_heads, head_dim:</dt><dd>Integers describing the
          cache geometry.</dd>
          <dt>kv_dtype:</dt><dd>The element type of the stored cache, from the
          "KVSP Element Types" registry (<xref target="iana-desc"/>).</dd>
          <dt>positional:</dt><dd>The positional encoding variant, from the
          "KVSP Positional Encodings" registry, optionally followed by ";" and
          parameters in the registered syntax.  Parameters that are absent
          take the defaults of the model, and a descriptor <bcp14>MUST</bcp14> include them
          whenever the model is run with non-default values.</dd>
          <dt>block_tokens:</dt><dd>The block size B in tokens.</dd>
          <dt>layout:</dt><dd>The layout identifier for codec "raw"
          (<xref target="container"/>); this document defines
          "kvsp-layout-1".</dd>
        </dl>
        <t>The Compatibility ID is:</t>
        <artwork>
   compat_id = SHA-256( "KVSP-COMPAT-1" || 0x00 || JCS(descriptor) )
</artwork>
        <t>where JCS is the JSON Canonicalization Scheme
        <xref target="RFC8785"/> and || denotes concatenation.  The
        Compatibility ID is carried as hex32.  Two caches <bcp14>MUST NOT</bcp14> be
        substituted for each other unless their Compatibility IDs are equal.
        Implementations <bcp14>MUST</bcp14> compute the Compatibility ID from the descriptor
        rather than accept it from a peer without verification whenever the
        descriptor is available.</t>
      </section>

      <section anchor="keys">
        <name>Scopes and Block Keys</name>
        <t>Each scope has a Scope Key of at least 32 random octets, shared by
        the principals of the scope through means outside this document.
        The Scope ID is:</t>
        <artwork>
   scope_id = first 16 octets of
              SHA-256( "KVSP-SCOPE-1" || 0x00 || scope_key )
</artwork>
        <t>The Block Key of block i of a token sequence t[0], t[1], ... is
        defined recursively, with K[-1] equal to 32 zero octets:</t>
        <artwork>
   K[i] = HMAC-SHA-256( scope_key,
                        "KVSP-BLOCK-1" || 0x00 || compat_id ||
                        K[i-1] ||
                        uint32(t[i*B]) || ... || uint32(t[i*B+B-1]) )
</artwork>
        <t>HMAC is defined in <xref target="RFC2104"/>.  compat_id is the 32
        raw octets, and uint32() is the token in four octets in network byte
        order.  Because K[i] covers K[i-1], a Block Key identifies the whole
        prefix [0, (i+1)*B) and not only the tokens of block i.  Only
        complete blocks have keys; the tokens after the last complete block
        are not shared.  Block Keys are carried as hex32.</t>
        <t>A deployment that wants to share a public prefix (for example, a
        common system prompt) across tenants creates a dedicated shared scope
        for it.  Caches <bcp14>MUST NOT</bcp14> be shared across scopes otherwise.</t>
      </section>

      <section anchor="container">
        <name>KV Block Container</name>
        <t>Blocks are exchanged in the KV Block Container.  A container holds
        a contiguous range of layers of one block:</t>
        <figure anchor="fig-container">
          <name>KV Block Container Header</name>
          <artwork type="ascii-art">
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  Magic ("KVB1" = 0x4B564231)                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Version    |     Flags     |             Codec             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          First Layer                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Layer Count                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Token Count                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      Layer Count entries of { K Length (64), V Length (64) }  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data: for each layer in ascending order, K octets, V octets  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
</artwork>
        </figure>
        <t>Version is 1.  Flags are reserved and set to zero.  Codec is a value
        from the "KVSP Codecs" registry (<xref target="iana-codecs"/>).
        Integers are in network byte order.  The ascending layer order allows
        a receiver to start using layer l as soon as it has been received,
        overlapping computation with loading.</t>
        <t>For codec 0x0001 ("raw"), the K and V sections of a layer each hold
        Token Count x kv_heads x head_dim elements of kv_dtype, in row-major
        order with the token index varying slowest and the dimension index
        fastest, each element in little-endian octet order; this is layout
        "kvsp-layout-1".  Other codecs (for example,
        quantized or entropy-coded caches) define their own section
        encoding.</t>
        <t>The layer digest of layer l is the SHA-256 of its K section followed
        by its V section.  The digest of a replica is the SHA-256 of the
        concatenation of the layer digests of all layers, in ascending
        order.  This construction lets a client that retrieves a range of
        layers verify each of them against the layer digests listed in the
        Replica Record, and verify that list against the replica digest.</t>
      </section>

      <section anchor="records">
        <name>Records</name>
        <t>The following JSON objects are used in requests and responses.
        Unknown members <bcp14>MUST</bcp14> be ignored.</t>
        <t>Replica Record:</t>
        <sourcecode type="json">
{
  "block_key": "&lt;hex32&gt;",
  "compat_id": "&lt;hex32&gt;",
  "scope_id": "&lt;hex16&gt;",
  "holder": "https://h1.example/kvsp/",
  "codec": 1,
  "size": 2097152,
  "digest": "sha-256:&lt;hex32&gt;",
  "layer_digests": ["&lt;hex32&gt;", "&lt;hex32&gt;", "..."],
  "residency": [
    { "tier": "device", "layers": [0, 15] },
    { "tier": "host",   "layers": [16, 31],
      "read_mbit_per_s": 400000 }
  ],
  "ttl_ms": 600000
}
</sourcecode>
        <t>"layer_digests" lists the layer digests (<xref target="container"/>)
        in layer order; it is <bcp14>REQUIRED</bcp14> for replicas that are served by layer
        range.  The residency array states which layers reside in which
        tier.  Layer ranges are inclusive and <bcp14>MUST NOT</bcp14> overlap.  The optional
        "read_mbit_per_s" of a residency entry states the rate at which the
        holder can read that tier.  A holder that keeps some layers of a block
        in accelerator memory and the others in host memory, to overlap layer
        loading with computation, reports this split so that placement can
        account for it.</t>
        <t>A replica can also be served through Media over QUIC Transport
        <xref target="I-D.li-kvcache-efficient-transmission"/>.  Its record then has a
        "holder" that is a "moqt" URI of the publishing endpoint or relay and
        a member "moqt_namespace" holding the base64url encoding (without
        padding) of the five Track Namespace Fields, each preceded by its
        length as one octet.  The publisher of that namespace is the holder
        for the purposes of authorization, and the digests have the meaning
        defined in this section, which the MOQT object digests reproduce for
        codec "raw".</t>
        <t>Instance Status Record:</t>
        <sourcecode type="json">
{
  "instance": "https://ii7.example/kvsp/",
  "epoch": 12,
  "state": "active",
  "running": 18, "queued": 3, "batch_size": 32,
  "kv_utilization": 0.71,
  "prefill_tokens_per_s": 42000,
  "decode_tokens_per_s": 3100,
  "links": [
    { "holder": "https://h1.example/kvsp/",
      "mbit_per_s": 200000, "rtt_us": 12 }
  ],
  "requested_state": null,
  "ttl_ms": 3000
}
</sourcecode>
        <t>Session Checkpoint:</t>
        <sourcecode type="json">
{
  "session": "6f1d0a9e-2c55-4d7e-9a0e-5b7f2f9f6a11",
  "epoch": 1,
  "compat_id": "&lt;hex32&gt;",
  "scope_id": "&lt;hex16&gt;",
  "prompt_blocks": ["&lt;hex32&gt;", "&lt;hex32&gt;"],
  "prompt_tail": [374, 264],
  "generated": [1296, 315, 279],
  "output_offset": 3,
  "sampler": { "method": "top-p", "params": {"p": 0.9},
               "seed": 7, "step": 3, "deterministic": false }
}
</sourcecode>
        <t>The session identifier is a UUID <xref target="RFC9562"/>.
        output_offset is the number of output tokens covered by the
        checkpoint.  prompt_tail holds the prompt tokens after the last
        complete prompt block.  "sampler.deterministic" is true only if every
        instance eligible to resume the session produces bit-identical
        output tokens from the same state, for example greedy decoding with
        batch-invariant kernels on identical software and hardware.</t>
      </section>
    </section>

    <section anchor="http">
      <name>HTTP Usage</name>
      <t>KVSP is an HTTP application in the sense of BCP 56
      <xref target="RFC9205"/>.  Each role is reached through a base URI
      configured by the operator; KVSP defines no well-known URI and no
      fixed path prefix outside that base.  The resource paths in
      <xref target="tbl-resources"/> are relative to the base URI of the
      role.  Clients <bcp14>MUST</bcp14> use HTTPS with TLS 1.3 <xref target="RFC8446"/> or
      later, and both sides <bcp14>MUST</bcp14> authenticate with certificates.  HTTP/2
      <xref target="RFC9113"/> or HTTP/3 <xref target="RFC9114"/> <bcp14>SHOULD</bcp14>
      be used.</t>
      <t>Request and response bodies, other than block data, use the media
      type "application/kvsp+json".  Errors are reported with Problem
      Details <xref target="RFC9457"/> using the types defined in
      <xref target="errors"/>.  Clients <bcp14>MUST</bcp14> handle status codes not
      listed in this document according to their class
      <xref target="RFC9110"/>.  Responses other than block data <bcp14>MUST</bcp14> carry
      "Cache-Control: no-store".  KVSP defines no new HTTP fields; request
      parameters are carried in the request body, in the path below the base
      URI, or in the query component, which BCP 56 permits for resources
      under the application's control.</t>
      <table anchor="tbl-resources">
        <name>KVSP Resources</name>
        <thead>
          <tr><th>Role</th><th>Method and Path</th><th>Purpose</th></tr>
        </thead>
        <tbody>
          <tr><td>IS</td><td>POST lookup</td><td>Longest-prefix lookup (<xref target="lookup"/>)</td></tr>
          <tr><td>IS</td><td>PUT, DELETE replicas/{holder-id}/{block_key}</td><td>Publish, withdraw (<xref target="index"/>)</td></tr>
          <tr><td>Holder</td><td>GET, PUT blocks/{compat}/{scope}/{block_key}</td><td>Retrieve, store (<xref target="transfer"/>)</td></tr>
          <tr><td>Holder</td><td>POST leases; PUT, DELETE leases/{id}</td><td>Leases (<xref target="leases"/>)</td></tr>
          <tr><td>PC</td><td>PUT instances/{instance-id}/status</td><td>Status (<xref target="status"/>)</td></tr>
          <tr><td>PC</td><td>PUT sessions/{session}/checkpoint</td><td>Checkpoint (<xref target="migration"/>)</td></tr>
          <tr><td>II</td><td>POST prefetch; GET prefetch/{id}</td><td>Prefetch (<xref target="prefetch"/>)</td></tr>
          <tr><td>II</td><td>POST sessions/{session}/resume</td><td>Migration (<xref target="migration"/>)</td></tr>
          <tr><td>II</td><td>POST reshard</td><td>Resharding (<xref target="reshard"/>)</td></tr>
          <tr><td>II</td><td>PUT state</td><td>Lifecycle (<xref target="status"/>)</td></tr>
        </tbody>
      </table>
      <t>{holder-id} and {instance-id} are the base64url encoding without
      padding <xref target="RFC4648"/> of the SHA-256 of the holder's or
      instance's base URI, which gives each a fixed-length path-safe
      identifier.</t>
    </section>

    <section anchor="procedures">
      <name>Procedures</name>

      <section anchor="index">
        <name>Publishing and Withdrawing Replicas</name>
        <t>A holder publishes each replica it is willing to serve with PUT,
        carrying a Replica Record.  The Index Service <bcp14>MUST</bcp14> verify that the
        authenticated client is the holder named in the record or is
        authorized to publish on its behalf, and that the client is
        authorized for the record's scope.  A replica record expires after
        ttl_ms; the IS caps ttl_ms at a configured maximum.  Holders refresh
        records before expiry with the same PUT, which is idempotent.  A
        holder <bcp14>MUST</bcp14> withdraw a record with DELETE before or immediately after
        evicting the replica, and <bcp14>MUST NOT</bcp14> evict a replica covered by an
        unexpired lease (<xref target="leases"/>).</t>
        <t>The index is advisory: a holder can evict an unleased replica
        before the withdrawal reaches the IS.  Clients therefore treat a 404
        from a holder as a cache miss and continue with the next replica or
        with recomputation.</t>
      </section>

      <section anchor="lookup">
        <name>Lookup</name>
        <t>A client (normally the PC) computes the Block Keys of the prompt
        and sends them, in prefix order, to the IS:</t>
        <sourcecode type="json">
POST {is-base}lookup
{
  "scope_id": "&lt;hex16&gt;",
  "compat_id": "&lt;hex32&gt;",
  "block_keys": ["&lt;hex32&gt;", "&lt;hex32&gt;", "&lt;hex32&gt;"],
  "targets": ["https://ii7.example/kvsp/",
              "https://ii9.example/kvsp/"]
}
</sourcecode>
        <t>The IS returns "matched", the number of leading keys for which at
        least one unexpired replica exists, and for each matched key the
        replica records.  Because Block Keys are chained, the IS <bcp14>MUST</bcp14> stop at
        the first key without a replica.  When "targets" is present and the
        IS has the needed status and link information, it also returns, for
        each target instance, "fetch_us" (the estimated time to make the
        matched blocks resident at that instance, <xref target="placement"/>)
        and "recompute_us" (the estimated time to prefill the same tokens on
        that instance).  The IS <bcp14>MUST NOT</bcp14> return replicas of a scope for which
        the client is not authorized.</t>
      </section>

      <section anchor="status">
        <name>Instance Status and Lifecycle</name>
        <t>Each inference instance sends its Instance Status Record to the PC
        at an interval no longer than one third of its ttl_ms.  The PC
        considers an instance failed when its record expires.  The "epoch" of
        an instance <bcp14>MUST</bcp14> increase every time it restarts; the start time of
        the instance in milliseconds since the Unix epoch satisfies this
        without persistent state, provided that the instance's clock does not
        move backward across restarts; otherwise the instance persists a
        counter.  The PC <bcp14>MUST</bcp14> discard records with an epoch
        lower than the highest it has seen for that instance.</t>
        <t>Instances move between the states active, draining, sleeping, and
        failed.  An instance whose load has stayed below a configured
        threshold for a configured time <bcp14>MAY</bcp14> ask to sleep by setting
        "requested_state" to "sleeping".  The PC answers the status PUT with
        200 and a body {"state": "draining"} to accept, or {"state":
        "active"} to refuse.  The PC can also change the state of an
        instance with PUT state on the instance.</t>
        <ul>
          <li>The PC <bcp14>MUST NOT</bcp14> assign new requests or send prefetch directives
          to an instance that is not active.</li>
          <li>A draining instance <bcp14>MUST</bcp14> complete its in-flight sessions and
          <bcp14>MUST NOT</bcp14> accept new ones; when it has none left it enters sleeping
          and reports it.</li>
          <li>A sleeping instance becomes active only on a state change from
          the PC.</li>
        </ul>
      </section>

      <section anchor="placement">
        <name>Placement Inputs</name>
        <t>The PC chooses an instance for each request.  The policy is local,
        but it is expected to compare, for each candidate, the cost of
        loading cached blocks with the cost of recomputing them.  The
        following estimates <bcp14>SHOULD</bcp14> be used, by the IS when it answers a
        lookup with targets and by the PC otherwise:</t>
        <artwork>
fetch_us(i)     = max over sources s of
                    ( rtt_us(i,s)
                      + 8 * bytes_from(s) / mbit_per_s(i,s) )
                  for blocks not already resident at i
recompute_us(i) = 1e6 * tokens / prefill_tokens_per_s(i)
wait_us(i)      = queued(i) * mean service time at i
cost(i)         = wait_us(i)
                  + min( fetch_us(i) + recompute_us_of_unmatched(i),
                         recompute_us_of_all(i) )
</artwork>
        <t>where bytes_from(s) is the volume the instance would load from
        source s, and mbit_per_s(i,s) is the link rate reported by instance i
        for source s, reduced, for layers that the source holds in a storage
        tier, to the "read_mbit_per_s" of that tier in the Replica Record.
        The PC <bcp14>SHOULD</bcp14>
        assign the request to an active instance with the lowest cost.  When
        no active instance has acceptable cost or capacity, the PC <bcp14>MAY</bcp14> start
        a new instance and <bcp14>SHOULD</bcp14> send it a prefetch directive at once, so
        that cache loading overlaps the instance start.</t>
      </section>

      <section anchor="prefetch">
        <name>Prefetch Directives</name>
        <t>To load caches into an instance before a request reaches it, the PC
        sends a prefetch directive:</t>
        <sourcecode type="json">
POST {ii-base}prefetch
{
  "directive": "a2b7c9e0-1f3d-4c1a-8f5b-6d2e9a7c3b10",
  "request_ref": "opaque-value-from-PC",
  "scope_id": "&lt;hex16&gt;",
  "compat_id": "&lt;hex32&gt;",
  "blocks": [
    { "block_key": "&lt;hex32&gt;",
      "sources": ["https://h1.example/kvsp/"] }
  ],
  "layer_order": "ascending",
  "deadline_ms": 50,
  "lease_ms": 2000
}
</sourcecode>
        <t>The instance answers 202 (Accepted) with a Location field naming
        the directive's status resource, and processes the directive while it
        continues to execute current work; prefetch <bcp14>MUST NOT</bcp14> block execution
        of requests already admitted.  For each block it does not already
        hold, the instance acquires a lease of lease_ms at one of the listed
        sources (<xref target="leases"/>), so that the source cannot evict the
        block while it is being loaded, retrieves the block
        (<xref target="transfer"/>), verifies the digest, places it in its
        tiers, and publishes its new replica (<xref target="index"/>).  The
        state of the directive ("pending", "partial", "complete", or
        "failed", with the list of loaded keys) can be read with GET.  If the
        deadline expires, the instance stops loading and reports "partial";
        the request is then served with the loaded prefix and the remaining
        tokens are recomputed.</t>
        <t>"request_ref" is an opaque value that lets the PC associate the
        directive with the inference request it will route; how the request
        carries the value is outside the scope of this document.</t>
      </section>

      <section anchor="leases">
        <name>Leases</name>
        <t>POST leases on a holder, with a body listing block keys and
        "ttl_ms", creates a lease and returns its identifier and the granted
        TTL.  PUT on the lease renews it; DELETE releases it.  While a lease is
        valid the holder <bcp14>MUST NOT</bcp14> evict the leased replicas.  A holder <bcp14>MUST</bcp14>
        enforce a per-client quota on leased octets and lease TTL and returns
        the problem type "kvsp-lease-quota" when a request exceeds it.  A holder
        that must evict leased data due to a fault <bcp14>MUST</bcp14> first withdraw the
        affected replica records.</t>
      </section>

      <section anchor="transfer">
        <name>Block Retrieval and Storage</name>
        <t>Block resources are named blocks/{compat}/{scope}/{block_key}, where
        {compat} is the Compatibility ID (hex32), {scope} the Scope ID (hex16),
        and {block_key} the Block Key (hex32), so that retrieval and storage
        address the same resource.  GET on it returns the container
        (<xref target="container"/>) with media type
        "application/kvsp-block".  The query parameter "layers=a-b" requests
        the inclusive layer range a..b; a holder that does not hold those
        layers returns 404 with problem type "kvsp-layers-unavailable".  The
        query parameter "codec=n" requests a codec; a holder <bcp14>MAY</bcp14> transcode.
        Holders <bcp14>MUST</bcp14> include a "Repr-Digest" field <xref target="RFC9530"/>
        with the SHA-256 of the returned container.  Block representations
        are immutable but confidential; holders <bcp14>SHOULD</bcp14> send "Cache-Control:
        private, immutable" with a max-age no longer than the remaining TTL
        of the replica.</t>
        <t>Clients <bcp14>MUST</bcp14> verify Repr-Digest.  When the returned codec equals the
        replica's codec, clients <bcp14>MUST</bcp14> also verify the layer digests of the
        returned layers against the Replica Record, and, when all layers were
        retrieved, the replica digest.  Transcoded representations are
        protected only by Repr-Digest, so clients that need producer-level
        integrity request the replica's codec.</t>
        <t>PUT on the block resource with "If-None-Match: *" stores a block at
        a holder.  The holder <bcp14>MUST</bcp14> reject
        the request if the client is not authorized for the scope.  The key
        identifies the token prefix, not the stored octets, so a holder that
        already has a replica for the key returns 412 and keeps its copy.</t>
      </section>

      <section anchor="migration">
        <name>Session Checkpoints and Migration</name>
        <t>An instance that serves a session <bcp14>SHOULD</bcp14> publish a Session
        Checkpoint to the PC with PUT sessions/{session}/checkpoint whenever
        a block of generated tokens completes, and at least every 64 output
        tokens.  It also publishes replicas for the generated blocks, so that
        their KV cache survives the instance.  The PC records the checkpoint
        with the highest output_offset for the current session epoch and
        <bcp14>MUST</bcp14> reject a checkpoint with a lower epoch with problem type
        "kvsp-stale-epoch".</t>
        <t>Output continuity depends on the sampler.  If the checkpoint states
        that the sampler is deterministic, a resumed instance regenerates
        exactly the tokens the failed instance produced after the last
        checkpoint, and the front end discards the duplicates by index.
        Otherwise the instance <bcp14>MUST NOT</bcp14> release an output token to the front
        end before the PC has acknowledged a checkpoint that covers it; it can
        checkpoint several tokens at a time to bound the added latency.
        Every output token carries its index and the session epoch, and front
        ends <bcp14>MUST</bcp14> discard tokens that carry a lower epoch than one they have
        already seen for the session.</t>
        <t>When the instance fails, the PC selects a new instance as in
        <xref target="placement"/>, increments the session epoch, and sends
        POST sessions/{session}/resume to it with the latest checkpoint and
        the new epoch.  The new instance:</t>
        <ol>
          <li>loads the blocks listed in the checkpoint and the generated
          blocks published by the failed instance;</li>
          <li>recomputes the KV cache of prompt_tail and of generated tokens
          not covered by loaded blocks;</li>
          <li>restores the sampler with the given method, parameters, seed,
          and step; and</li>
          <li>continues generation and labels each output token with its
          index, starting at output_offset, so that the front end delivers
          every index exactly once.</li>
        </ol>
        <t>Any message from the failed instance carrying the old epoch <bcp14>MUST</bcp14> be
        rejected.  Under these rules the output already delivered is never
        repeated or altered, and the client receives a single consistent
        sample.</t>
      </section>

      <section anchor="reshard">
        <name>Sequence-Sharded Caches and Resharding</name>
        <t>For long contexts, a parallel group can split one context by token
        range across several instances.  The PC describes the split in a
        Shard Map:</t>
        <sourcecode type="json">
{
  "map": "c3f8...e1", "version": 4,
  "shards": [
    { "tokens": [0, 32768],
      "instance": "https://ii1.example/kvsp/" },
    { "tokens": [32768, 65536],
      "instance": "https://ii2.example/kvsp/" },
    { "tokens": [65536, 98304],
      "instance": "https://ii3.example/kvsp/" }
  ]
}
</sourcecode>
        <t>Token ranges are half-open, contiguous, and non-overlapping.  The
        computing demand of a context changes between phases: prefill is
        compute-bound and benefits from many instances, while early decoding
        steps need fewer instances and later ones need more memory.  The PC
        can therefore change the group between iterations.  To move from
        version v to v+1, it sends each instance in either version a reshard
        plan with POST reshard:</t>
        <sourcecode type="json">
{
  "map": "c3f8...e1", "from": 4, "to": 5,
  "deadline_ms": 200,
  "retain":  [[0, 32768]],
  "fetch":   [{ "tokens": [32768, 49152],
                "from": "https://ii2.example/kvsp/" }],
  "release": []
}
</sourcecode>
        <t>The following rules apply:</t>
        <ul>
          <li>A token range assigned to the same instance in both versions
          <bcp14>MUST</bcp14> appear in "retain" for that instance and <bcp14>MUST NOT</bcp14> be
          transferred.  Retained ranges keep their memory in place, so a
          change of parallelism costs no transfer for them.</li>
          <li>An instance acknowledges the plan with 200 after it has loaded
          all ranges listed in "fetch".</li>
          <li>Version v remains authoritative until all instances have
          acknowledged; the PC then sends POST reshard with {"map": ...,
          "commit": v+1} to all members, after which instances release the
          ranges listed in "release".</li>
          <li>If any instance fails to acknowledge within deadline_ms, the PC
          sends POST reshard with {"map": ..., "abort": v+1}, and the members
          discard the ranges they fetched for version v+1.</li>
        </ul>
      </section>
    </section>

    <section anchor="errors">
      <name>Error Handling</name>
      <table anchor="tbl-problems">
        <name>KVSP Problem Types</name>
        <thead>
          <tr><th>Type suffix</th><th>Status</th><th>Meaning</th></tr>
        </thead>
        <tbody>
          <tr><td>kvsp-not-found</td><td>404</td><td>Block, lease, session, or directive unknown</td></tr>
          <tr><td>kvsp-scope-denied</td><td>403</td><td>Client not authorized for the scope</td></tr>
          <tr><td>kvsp-incompatible</td><td>409</td><td>Compatibility ID mismatch</td></tr>
          <tr><td>kvsp-lease-quota</td><td>429</td><td>Lease quota exceeded</td></tr>
          <tr><td>kvsp-stale-epoch</td><td>409</td><td>Epoch older than current</td></tr>
          <tr><td>kvsp-invalid-state</td><td>409</td><td>Operation not allowed in the instance state</td></tr>
          <tr><td>kvsp-digest-mismatch</td><td>422</td><td>Stored data does not match its digest</td></tr>
          <tr><td>kvsp-plan-conflict</td><td>409</td><td>Reshard plan does not match the current map version</td></tr>
          <tr><td>kvsp-layers-unavailable</td><td>404</td><td>Requested layers not held</td></tr>
        </tbody>
      </table>
      <t>The type URI of each problem is "https://iana.org/assignments/kvsp#"
      followed by the type suffix.  These types are specific to KVSP and are
      therefore registered in a KVSP registry rather than in the "HTTP
      Problem Types" registry, which accepts only widely applicable types
      (<xref target="RFC9457" section="4.2"/>).  A client that receives a block whose digest does not
      match <bcp14>MUST</bcp14> discard it, <bcp14>SHOULD</bcp14> report it to the IS by withdrawing the
      replica on the holder's behalf if authorized, and continues with
      another replica or recomputation.</t>
    </section>

    <section anchor="ops">
      <name>Operational Considerations</name>
      <ul>
        <li>Replica TTLs trade index accuracy against update load; a TTL of a
        few minutes with refresh at one third of the TTL is a reasonable
        start.</li>
        <li>KVSP uses durations rather than timestamps, so it does not depend
        on synchronized clocks.</li>
        <li>The index can be partitioned by Block Key, since keys are
        uniformly distributed.</li>
        <li>Deployment can be incremental: instances that do not implement
        KVSP simply recompute; holders can be added without changes to
        inference engines that already publish their tiers.</li>
        <li>Implementations <bcp14>SHOULD</bcp14> report lookup hit ratio, fetched versus
        recomputed tokens, lease rejections, digest failures, and
        migrations.</li>
      </ul>
    </section>

    <section anchor="security">
      <name>Security Considerations</name>
      <t>All KVSP exchanges are authenticated with mutual TLS.  Each role
      <bcp14>MUST</bcp14> authorize requests by the authenticated identity: only holders
      publish replicas for themselves, only the PC sends directives to
      instances and records checkpoints, and access to blocks requires
      authorization for the block's scope.</t>
      <t>Cache poisoning.  A malicious holder could serve incorrect data for
      a valid key, which would silently corrupt model output.  Digests in
      replica records and Repr-Digest detect corruption in transit and at
      honest holders but not a holder that publishes a wrong digest.
      Deployments <bcp14>MUST</bcp14> therefore restrict publication and service of blocks
      to holders trusted for the scope.  Where stronger assurance is needed,
      the producing instance can publish the digest itself, and consumers
      accept only replicas whose digest matches that of the producer.</t>
      <t>Cross-tenant inference.  Whether a cache lookup hits, or how fast a
      request is served, reveals whether another principal processed the
      same prefix.  Scopes prevent such disclosure across tenants because
      keys of different scopes are unrelated and lookups are confined to
      authorized scopes.  Within a scope, any principal can test whether
      another principal of the same scope has processed a guessed prefix,
      by lookup or by timing; scopes therefore <bcp14>SHOULD</bcp14> contain only
      principals that may learn each other's prompts, and shared scopes
      <bcp14>SHOULD</bcp14> be used only for content that is public within the sharing
      group.</t>
      <t>Denial of service.  Leases reserve holder memory.  Quotas on leased
      octets, lease TTL, the number of pending directives, and lookup size
      are <bcp14>REQUIRED</bcp14>.  Directives can make an instance load data; instances
      <bcp14>MUST</bcp14> accept them only from the PC.</t>
      <t>Session takeover.  Session epochs fence a failed or partitioned
      instance, so that only one instance can extend a session.</t>
    </section>

    <section anchor="privacy">
      <name>Privacy Considerations</name>
      <t>KV caches are derived from user prompts and outputs, and model
      inversion techniques can recover input text from cached keys and
      values.  KV cache data, checkpoints (which contain output tokens), and
      replica records <bcp14>MUST</bcp14> be treated with the same confidentiality as the
      prompts and outputs themselves <xref target="RFC6973"/>.  Holders
      <bcp14>SHOULD</bcp14> encrypt stored blocks and <bcp14>SHOULD</bcp14> apply the retention limits of
      the originating service.</t>
      <t>Plain hashes of token sequences can be inverted by enumerating
      likely prompts.  Block Keys are keyed with the Scope Key, so the IS and
      other parties without the key cannot test guesses.  The Scope Key <bcp14>MUST</bcp14>
      NOT be disclosed to the IS.</t>
    </section>

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

      <section anchor="iana-media">
        <name>Media Types</name>
        <t>IANA is requested to register the following media types in the
        "Media Types" registry <xref target="RFC6838"/>.</t>
        <t>Type name: application; Subtype name: kvsp+json; Required
        parameters: N/A; Optional parameters: N/A; Encoding considerations:
        binary (UTF-8 JSON); Security considerations: see
        <xref target="security"/> of RFC XXXX; Interoperability
        considerations: none; Published specification: RFC XXXX;
        Applications that use this media type: KV cache storage and
        distributed inference systems; Fragment identifier considerations:
        none; Additional information: Deprecated alias names for this type:
        N/A, Magic number(s): N/A, File extension(s): N/A, Macintosh file type
        code(s): N/A; Person and email address to contact: IETF
        &lt;iesg@ietf.org&gt;; Intended usage: COMMON; Restrictions on usage:
        none; Author: IETF; Change controller: IETF.</t>
        <t>Type name: application; Subtype name: kvsp-block; Required
        parameters: N/A; Optional parameters: N/A; Encoding considerations:
        binary; Security considerations: see <xref target="security"/> of RFC
        XXXX; Interoperability considerations: none; Published specification:
        <xref target="container"/> of RFC XXXX; Applications that use this
        media type: KV cache storage and distributed inference systems;
        Fragment identifier considerations: none; Additional information:
        Deprecated alias names for this type: N/A, Magic number(s): 0x4B564231
        at offset 0, File extension(s): N/A, Macintosh file type code(s): N/A;
        Person and email address to contact: IETF &lt;iesg@ietf.org&gt;;
        Intended usage: COMMON; Restrictions on usage: none; Author: IETF;
        Change controller: IETF.</t>
      </section>

      <section anchor="iana-registries">
        <name>KVSP Registries</name>
        <t>IANA is requested to create a registry group named "KV Cache
        Storage Protocol (KVSP)" at "https://www.iana.org/assignments/kvsp"
        with the following registries.  New assignments use the
        Specification Required policy <xref target="RFC8126"/>.  The
        designated experts should check that a registration is specified
        precisely enough for independent implementations to interoperate, and
        that names are lowercase ASCII and do not duplicate existing
        entries.</t>
        <section anchor="iana-problems">
          <name>KVSP Problem Types</name>
          <t>Columns: Type Suffix, Title, Recommended HTTP Status Code,
          Reference.  The type URI is "https://iana.org/assignments/kvsp#"
          followed by the suffix.  Initial contents: the entries of
          <xref target="tbl-problems"/>, with reference RFC XXXX.</t>
        </section>
        <section anchor="iana-codecs">
          <name>KVSP Codecs</name>
          <t>Columns: Value (16 bits), Name (lowercase ASCII letters, digits,
          and "-", at most 32 characters), Description, Reference.  Initial
          contents: 0x0000 Reserved; 0x0001 "raw", uncompressed elements in
          layout "kvsp-layout-1" (<xref target="container"/>).  Values
          0xFF00-0xFFFF are reserved for Private Use.</t>
        </section>
        <section anchor="iana-desc">
          <name>KVSP Element Types and Positional Encodings</name>
          <t>"KVSP Element Types" has columns Name, Description, Reference;
          initial contents: "bf16" (bfloat16), "fp16" (IEEE 754 binary16),
          "fp32" (IEEE 754 binary32), "fp8-e4m3", and "fp8-e5m2" (8-bit
          floating point with 4- or 5-bit exponent).  "KVSP Positional
          Encodings" has columns Name, Parameter Syntax, Description,
          Reference; initial contents: "rope" (rotary position embedding;
          optional parameters: "theta=" followed by the base as a decimal
          integer, and ",scaling=" followed by a scaling method name) and
          "none".</t>
        </section>
        <section anchor="iana-tiers">
          <name>KVSP Storage Tiers</name>
          <t>Columns: Name (lowercase ASCII), Description, Reference.  Initial
          contents: "device" (accelerator memory), "host" (host memory,
          including pinned memory), "local-storage" (storage attached to the
          holder), "remote-memory" (disaggregated memory pool),
          "remote-storage" (networked storage).</t>
        </section>
        <section anchor="iana-states">
          <name>KVSP Instance States</name>
          <t>Columns: Name (lowercase ASCII), Description, Reference.  Initial
          contents: "active", "draining", "sleeping", "failed", as defined in
          <xref target="status"/>.</t>
        </section>
      </section>
    </section>
  </middle>

  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <reference anchor="RFC2104" target="https://www.rfc-editor.org/info/rfc2104">
  <front>
    <title>HMAC: Keyed-Hashing for Message Authentication</title>
    <author fullname="H. Krawczyk" initials="H." surname="Krawczyk"/>
    <author fullname="M. Bellare" initials="M." surname="Bellare"/>
    <author fullname="R. Canetti" initials="R." surname="Canetti"/>
    <date month="February" year="1997"/>
    </front>
  <seriesInfo name="RFC" value="2104"/>
  <seriesInfo name="DOI" value="10.17487/RFC2104"/>
</reference>
        <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 fullname="S. Bradner" initials="S." surname="Bradner"/>
    <date month="March" year="1997"/>
    </front>
  <seriesInfo name="BCP" value="14"/>
  <seriesInfo name="RFC" value="2119"/>
  <seriesInfo name="DOI" value="10.17487/RFC2119"/>
</reference>
        <reference anchor="RFC4648" target="https://www.rfc-editor.org/info/rfc4648">
  <front>
    <title>The Base16, Base32, and Base64 Data Encodings</title>
    <author fullname="S. Josefsson" initials="S." surname="Josefsson"/>
    <date month="October" year="2006"/>
    </front>
  <seriesInfo name="RFC" value="4648"/>
  <seriesInfo name="DOI" value="10.17487/RFC4648"/>
</reference>
        <reference anchor="RFC6234" target="https://www.rfc-editor.org/info/rfc6234">
  <front>
    <title>US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)</title>
    <author fullname="D. Eastlake 3rd" initials="D." surname="Eastlake 3rd"/>
    <author fullname="T. Hansen" initials="T." surname="Hansen"/>
    <date month="May" year="2011"/>
    </front>
  <seriesInfo name="RFC" value="6234"/>
  <seriesInfo name="DOI" value="10.17487/RFC6234"/>
</reference>
        <reference anchor="RFC6838" target="https://www.rfc-editor.org/info/rfc6838">
  <front>
    <title>Media Type Specifications and Registration Procedures</title>
    <author fullname="N. Freed" initials="N." surname="Freed"/>
    <author fullname="J. Klensin" initials="J." surname="Klensin"/>
    <author fullname="T. Hansen" initials="T." surname="Hansen"/>
    <date month="January" year="2013"/>
    </front>
  <seriesInfo name="BCP" value="13"/>
  <seriesInfo name="RFC" value="6838"/>
  <seriesInfo name="DOI" value="10.17487/RFC6838"/>
</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 fullname="M. Cotton" initials="M." surname="Cotton"/>
    <author fullname="B. Leiba" initials="B." surname="Leiba"/>
    <author fullname="T. Narten" initials="T." surname="Narten"/>
    <date month="June" year="2017"/>
    </front>
  <seriesInfo name="BCP" value="26"/>
  <seriesInfo name="RFC" value="8126"/>
  <seriesInfo name="DOI" value="10.17487/RFC8126"/>
</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 fullname="B. Leiba" initials="B." surname="Leiba"/>
    <date month="May" year="2017"/>
    </front>
  <seriesInfo name="BCP" value="14"/>
  <seriesInfo name="RFC" value="8174"/>
  <seriesInfo name="DOI" value="10.17487/RFC8174"/>
</reference>
        <reference anchor="RFC8259" target="https://www.rfc-editor.org/info/rfc8259">
  <front>
    <title>The JavaScript Object Notation (JSON) Data Interchange Format</title>
    <author fullname="T. Bray" initials="T." role="editor" surname="Bray"/>
    <date month="December" year="2017"/>
    </front>
  <seriesInfo name="STD" value="90"/>
  <seriesInfo name="RFC" value="8259"/>
  <seriesInfo name="DOI" value="10.17487/RFC8259"/>
</reference>
        <reference anchor="RFC8446" target="https://www.rfc-editor.org/info/rfc8446">
  <front>
    <title>The Transport Layer Security (TLS) Protocol Version 1.3</title>
    <author fullname="E. Rescorla" initials="E." surname="Rescorla"/>
    <date month="August" year="2018"/>
    </front>
  <seriesInfo name="RFC" value="8446"/>
  <seriesInfo name="DOI" value="10.17487/RFC8446"/>
</reference>
        <reference anchor="RFC8785" target="https://www.rfc-editor.org/info/rfc8785">
  <front>
    <title>JSON Canonicalization Scheme (JCS)</title>
    <author fullname="A. Rundgren" initials="A." surname="Rundgren"/>
    <author fullname="B. Jordan" initials="B." surname="Jordan"/>
    <author fullname="S. Erdtman" initials="S." surname="Erdtman"/>
    <date month="June" year="2020"/>
    </front>
  <seriesInfo name="RFC" value="8785"/>
  <seriesInfo name="DOI" value="10.17487/RFC8785"/>
</reference>
        <reference anchor="RFC9110" target="https://www.rfc-editor.org/info/rfc9110">
  <front>
    <title>HTTP Semantics</title>
    <author fullname="R. Fielding" initials="R." role="editor" surname="Fielding"/>
    <author fullname="M. Nottingham" initials="M." role="editor" surname="Nottingham"/>
    <author fullname="J. Reschke" initials="J." role="editor" surname="Reschke"/>
    <date month="June" year="2022"/>
    </front>
  <seriesInfo name="STD" value="97"/>
  <seriesInfo name="RFC" value="9110"/>
  <seriesInfo name="DOI" value="10.17487/RFC9110"/>
</reference>
        <reference anchor="RFC9113" target="https://www.rfc-editor.org/info/rfc9113">
  <front>
    <title>HTTP/2</title>
    <author fullname="M. Thomson" initials="M." role="editor" surname="Thomson"/>
    <author fullname="C. Benfield" initials="C." role="editor" surname="Benfield"/>
    <date month="June" year="2022"/>
    </front>
  <seriesInfo name="RFC" value="9113"/>
  <seriesInfo name="DOI" value="10.17487/RFC9113"/>
</reference>
        <reference anchor="RFC9114" target="https://www.rfc-editor.org/info/rfc9114">
  <front>
    <title>HTTP/3</title>
    <author fullname="M. Bishop" initials="M." role="editor" surname="Bishop"/>
    <date month="June" year="2022"/>
    </front>
  <seriesInfo name="RFC" value="9114"/>
  <seriesInfo name="DOI" value="10.17487/RFC9114"/>
</reference>
        <reference anchor="RFC9205" target="https://www.rfc-editor.org/info/rfc9205">
  <front>
    <title>Building Protocols with HTTP</title>
    <author fullname="M. Nottingham" initials="M." surname="Nottingham"/>
    <date month="June" year="2022"/>
    </front>
  <seriesInfo name="BCP" value="56"/>
  <seriesInfo name="RFC" value="9205"/>
  <seriesInfo name="DOI" value="10.17487/RFC9205"/>
</reference>
        <reference anchor="RFC9457" target="https://www.rfc-editor.org/info/rfc9457">
  <front>
    <title>Problem Details for HTTP APIs</title>
    <author fullname="M. Nottingham" initials="M." surname="Nottingham"/>
    <author fullname="E. Wilde" initials="E." surname="Wilde"/>
    <author fullname="S. Dalal" initials="S." surname="Dalal"/>
    <date month="July" year="2023"/>
    </front>
  <seriesInfo name="RFC" value="9457"/>
  <seriesInfo name="DOI" value="10.17487/RFC9457"/>
</reference>
        <reference anchor="RFC9530" target="https://www.rfc-editor.org/info/rfc9530">
  <front>
    <title>Digest Fields</title>
    <author fullname="R. Polli" initials="R." surname="Polli"/>
    <author fullname="L. Pardue" initials="L." surname="Pardue"/>
    <date month="February" year="2024"/>
    </front>
  <seriesInfo name="RFC" value="9530"/>
  <seriesInfo name="DOI" value="10.17487/RFC9530"/>
</reference>
        <reference anchor="RFC9562" target="https://www.rfc-editor.org/info/rfc9562">
  <front>
    <title>Universally Unique IDentifiers (UUIDs)</title>
    <author fullname="K. Davis" initials="K." surname="Davis"/>
    <author fullname="B. Peabody" initials="B." surname="Peabody"/>
    <author fullname="P. Leach" initials="P." surname="Leach"/>
    <date month="May" year="2024"/>
    </front>
  <seriesInfo name="RFC" value="9562"/>
  <seriesInfo name="DOI" value="10.17487/RFC9562"/>
</reference>
      </references>
      <references>
        <name>Informative References</name>
        <reference anchor="RFC6920" target="https://www.rfc-editor.org/info/rfc6920">
  <front>
    <title>Naming Things with Hashes</title>
    <author fullname="S. Farrell" initials="S." surname="Farrell"/>
    <author fullname="D. Kutscher" initials="D." surname="Kutscher"/>
    <author fullname="C. Dannewitz" initials="C." surname="Dannewitz"/>
    <author fullname="B. Ohlman" initials="B." surname="Ohlman"/>
    <author fullname="A. Keranen" initials="A." surname="Keranen"/>
    <author fullname="P. Hallam-Baker" initials="P." surname="Hallam-Baker"/>
    <date month="April" year="2013"/>
    </front>
  <seriesInfo name="RFC" value="6920"/>
  <seriesInfo name="DOI" value="10.17487/RFC6920"/>
</reference>
        <reference anchor="RFC6973" target="https://www.rfc-editor.org/info/rfc6973">
  <front>
    <title>Privacy Considerations for Internet Protocols</title>
    <author fullname="A. Cooper" initials="A." surname="Cooper"/>
    <author fullname="H. Tschofenig" initials="H." surname="Tschofenig"/>
    <author fullname="B. Aboba" initials="B." surname="Aboba"/>
    <author fullname="J. Peterson" initials="J." surname="Peterson"/>
    <author fullname="J. Morris" initials="J." surname="Morris"/>
    <author fullname="M. Hansen" initials="M." surname="Hansen"/>
    <author fullname="R. Smith" initials="R." surname="Smith"/>
    <date month="July" year="2013"/>
    </front>
  <seriesInfo name="RFC" value="6973"/>
  <seriesInfo name="DOI" value="10.17487/RFC6973"/>
</reference>
        <reference anchor="I-D.li-cats-kv-cache-distribution" target="https://datatracker.ietf.org/doc/html/draft-li-cats-kv-cache-distribution-00">
  <front>
    <title>KV Cache Distribution for Distributed LLM Inference: Use Case and Requirements</title>
    <author fullname="Zhiqiang Li" initials="Z." surname="Li">
      <organization>China Mobile</organization>
    </author>
    <author fullname="Zongpeng Du" initials="Z." surname="Du">
      <organization>China Mobile</organization>
    </author>
    <author fullname="Junjie Wang" initials="J." surname="Wang">
      <organization>Centec</organization>
    </author>
    <author fullname="Wei Cheng" initials="W." surname="Cheng">
      <organization>Centec</organization>
    </author>
    <author fullname="Guoying Zhang" initials="G." surname="Zhang">
      <organization>Centec</organization>
    </author>
    <author fullname="Xun Sun" initials="X." surname="Sun">
      <organization>Inesa</organization>
    </author>
    <author fullname="Chunhao Zhao" initials="C." surname="Zhao">
      <organization>SAIA</organization>
    </author>
    <date day="4" month="July" year="2026"/>
    </front>
  <seriesInfo name="Internet-Draft" value="draft-li-cats-kv-cache-distribution-00"/>
</reference>
        <reference anchor="I-D.ietf-cats-framework" target="https://datatracker.ietf.org/doc/html/draft-ietf-cats-framework-24">
  <front>
    <title>A Framework for Computing-Aware Traffic Steering (CATS)</title>
    <author fullname="Cheng Li" initials="C." surname="Li">
      <organization>Huawei Technologies</organization>
    </author>
    <author fullname="Zongpeng Du" initials="Z." surname="Du">
      <organization>China Mobile</organization>
    </author>
    <author fullname="Mohamed Boucadair" initials="M." surname="Boucadair">
      <organization>Orange</organization>
    </author>
    <author fullname="Luis M. Contreras" initials="L. M." surname="Contreras">
      <organization>Telefonica</organization>
    </author>
    <author fullname="John Drake" initials="J." surname="Drake">
      <organization>Independent</organization>
    </author>
    <date day="2" month="April" year="2026"/>
    </front>
  <seriesInfo name="Internet-Draft" value="draft-ietf-cats-framework-24"/>
</reference>
        <reference anchor="I-D.li-kvcache-efficient-transmission" target="https://datatracker.ietf.org/doc/html/draft-li-kvcache-efficient-transmission-00">
          <front>
            <title>Layer-Aligned Key-Value (KV) Cache Streaming over Media over QUIC Transport (MOQT)</title>
            <author initials="Z." surname="Li" fullname="Zhiqiang Li"/>
            <author initials="Z." surname="Du" fullname="Zongpeng Du"/>
            <author initials="J." surname="Wang" fullname="Junjie Wang"/>
            <author initials="W." surname="Cheng" fullname="Wei Cheng"/>
            <author initials="Y." surname="Zhao" fullname="Ying Zhao"/>
            <author initials="C." surname="Zhao" fullname="Chunhao Zhao"/>
            <author initials="X." surname="Sun" fullname="Xun Sun"/>
            <date year="2026" month="November" day="2"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-li-kvcache-efficient-transmission-00"/>
        </reference>
        <reference anchor="I-D.li-rdma-kvcache-transfer-optimization" target="https://datatracker.ietf.org/doc/html/draft-li-rdma-kvcache-transfer-optimization-00">
          <front>
            <title>Key-Value (KV) Cache Block Transfer Protocol (KBTP) over RDMA</title>
            <author initials="Z." surname="Li" fullname="Zhiqiang Li"/>
            <author initials="Z." surname="Du" fullname="Zongpeng Du"/>
            <author initials="J." surname="Wang" fullname="Junjie Wang"/>
            <author initials="W." surname="Cheng" fullname="Wei Cheng"/>
            <author initials="Y." surname="Zhao" fullname="Ying Zhao"/>
            <author initials="C." surname="Zhao" fullname="Chunhao Zhao"/>
            <author initials="X." surname="Sun" fullname="Xun Sun"/>
            <date year="2026" month="November" day="2"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-li-rdma-kvcache-transfer-optimization-00"/>
        </reference>
        <reference anchor="PAGEDATTENTION" target="https://doi.org/10.1145/3600006.3613165">
          <front>
            <title>Efficient Memory Management for Large Language Model Serving with PagedAttention</title>
            <author initials="W." surname="Kwon" fullname="Woosuk Kwon"/>
            <author initials="Z." surname="Li" fullname="Zhuohan Li"/>
            <author initials="S." surname="Zhuang" fullname="Siyuan Zhuang"/>
            <author initials="Y." surname="Sheng" fullname="Ying Sheng"/>
            <author initials="L." surname="Zheng" fullname="Lianmin Zheng"/>
            <author initials="C. H." surname="Yu" fullname="Cody Hao Yu"/>
            <author initials="J." surname="Gonzalez" fullname="Joseph Gonzalez"/>
            <author initials="H." surname="Zhang" fullname="Hao Zhang"/>
            <author initials="I." surname="Stoica" fullname="Ion Stoica"/>
            <date year="2023" month="October"/>
          </front>
          <refcontent>Proceedings of the 29th ACM Symposium on Operating Systems Principles (SOSP)</refcontent>
        </reference>
        <reference anchor="MOONCAKE" target="https://www.usenix.org/conference/fast25/presentation/qin">
          <front>
            <title>Mooncake: Trading More Storage for Less Computation - A KVCache-centric Architecture for Serving LLM Chatbot</title>
            <author initials="R." surname="Qin" fullname="Ruoyu Qin"/>
            <author initials="Z." surname="Li" fullname="Zheming Li"/>
            <author initials="W." surname="He" fullname="Weiran He"/>
            <author initials="J." surname="Cui" fullname="Jialei Cui"/>
            <author initials="F." surname="Ren" fullname="Feng Ren"/>
            <author initials="M." surname="Zhang" fullname="Mingxing Zhang"/>
            <author initials="Y." surname="Wu" fullname="Yongwei Wu"/>
            <author initials="W." surname="Zheng" fullname="Weimin Zheng"/>
            <author initials="X." surname="Xu" fullname="Xinran Xu"/>
            <date year="2025" month="February"/>
          </front>
          <refcontent>23rd USENIX Conference on File and Storage Technologies (FAST 25)</refcontent>
        </reference>
      </references>
    </references>

    <section anchor="vectors" numbered="true">
      <name>Test Vectors</name>
      <t>Compatibility Descriptor after JCS canonicalization (one line,
      wrapped here for presentation):</t>
      <artwork>
{"block_tokens":4,"head_dim":128,"kv_dtype":"bf16","kv_heads":8,
"layers":32,"layout":"kvsp-layout-1","model":"example-model",
"model_digest":"sha-256:254e528c669a2084c9727298cacb83e8854e0a6f
f80e2bf18096b9dce0f7ebda","positional":"rope","tokenizer_digest"
:"sha-256:9f42a7a29dca3063ad26d7be7fe6a930fa38afb1a703e4f7ff538db
4e133b19c"}

compat_id = 5ed5c2f238839655d7c4029e12b7f31c
            af9e7421f4edb4057feee6dab760ee0f
</artwork>
      <t>Scope Key = octets 0x00, 0x01, ..., 0x1f.  Tokens = 128000, 791,
      6460, 374, 264, 1296, 315, 279 (two blocks of four):</t>
      <artwork>
scope_id = 6d1c6481cd428737da312c089db3832a
K[0]     = 9ca9d8b60f37f84726a27deb4cdc0d07
           5714607a131632c2366e30b99fb1163b
K[1]     = 653275884ca44f8945fd4c902611c467
           394b73656e64c78d8522648cb75aefc1
</artwork>
      <t>Container header for codec "raw", layers 0-1, four tokens, with K
      and V sections of 8192 octets each (4 tokens x 8 heads x 128 x 2
      octets):</t>
      <artwork>
4b 56 42 31 01 00 00 01 00 00 00 00 00 00 00 02
00 00 00 04 00 00 00 00 00 00 20 00 00 00 00 00
00 00 20 00 00 00 00 00 00 00 20 00 00 00 00 00
00 00 20 00
</artwork>
    </section>

    <section anchor="acknowledgments" numbered="false">
      <name>Acknowledgments</name>
      <t>The authors thank the participants of the CATS and distributed
      inference discussions for their feedback on the KV cache
      distribution use case.</t>
    </section>
  </back>
</rfc>