<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.30 (Ruby 3.4.7) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-denis-xet-02" category="info" submissionType="independent" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.31.0 -->
  <front>
    <title abbrev="XET">XET: Content-Addressable Storage Protocol for Efficient Data Transfer</title>
    <seriesInfo name="Internet-Draft" value="draft-denis-xet-02"/>
    <author initials="F." surname="Denis" fullname="Frank Denis">
      <organization>Independent Contributor</organization>
      <address>
        <email>fde@00f.net</email>
      </address>
    </author>
    <date year="2025"/>
    <keyword>Internet-Draft</keyword>
    <abstract>
      <?line 66?>

<t>This document specifies XET, a content-addressable storage (CAS) protocol designed for efficient storage and transfer of large files with chunk-level deduplication.</t>
      <t>XET uses content-defined chunking to split files into variable-sized chunks, aggregates chunks into containers called xorbs, and enables deduplication across files and repositories through cryptographic hashing.</t>
    </abstract>
    <note removeInRFC="true">
      <name>Discussion Venues</name>
      <t>Source for this draft and an issue tracker can be found at
    <eref target="https://github.com/jedisct1/draft-denis-xet"/>.</t>
    </note>
  </front>
  <middle>
    <?line 72?>

<section anchor="introduction">
      <name>Introduction</name>
      <t>Large-scale data storage and transfer systems face fundamental challenges in efficiency: storing multiple versions of similar files wastes storage space, and transferring unchanged data wastes bandwidth.
Traditional approaches such as file-level deduplication miss opportunities to share common content between different files, while fixed-size chunking fails to handle insertions and deletions gracefully.</t>
      <t>XET addresses these challenges through a content-addressable storage protocol that operates at the chunk level.
By using content-defined chunking with a rolling hash algorithm, XET creates stable chunk boundaries that remain consistent even when files are modified.</t>
      <t>This enables efficient deduplication not only within a single file across versions, but also across entirely different files that happen to share common content.</t>
      <t>The protocol is designed around several key principles:</t>
      <ul spacing="normal">
        <li>
          <t>Determinism: Given the same input data, any conforming implementation <bcp14>MUST</bcp14> produce identical chunks, hashes, and serialized formats, ensuring interoperability.</t>
        </li>
        <li>
          <t>Content Addressing: All objects (chunks, xorbs, files) are identified by cryptographic hashes of their content, enabling integrity verification and natural deduplication.</t>
        </li>
        <li>
          <t>Efficient Transfer: The reconstruction-based download model allows clients to fetch only the data they need, supporting range queries and parallel downloads.</t>
        </li>
        <li>
          <t>Algorithm Agility: The chunking and hashing algorithms are encapsulated in algorithm suites, enabling future evolution while maintaining compatibility within a deployment.</t>
        </li>
        <li>
          <t>Provider Agnostic: While originally developed for machine learning model and dataset storage, XET is a generic protocol applicable to any large file storage scenario.</t>
        </li>
      </ul>
      <t>This specification provides the complete details necessary for implementing interoperable XET clients and servers.
It defines the <tt>XET-GEARHASH-BLAKE3</tt> algorithm suite as the default, using <tt>Gearhash</tt> for content-defined chunking and <tt>BLAKE3</tt> for cryptographic hashing.</t>
      <section anchor="use-cases">
        <name>Use Cases</name>
        <t>XET is particularly well-suited for scenarios involving:</t>
        <ul spacing="normal">
          <li>
            <t>Machine Learning: Model checkpoints often share common layers and parameters across versions, enabling significant storage savings through deduplication.</t>
          </li>
          <li>
            <t>Dataset Management: Large datasets with incremental updates benefit from chunk-level deduplication, where only changed portions need to be transferred.</t>
          </li>
          <li>
            <t>Container Images: OCI container images share common base layers across different applications and versions. Content-defined chunking enables deduplication not only across image layers but also across similar content in unrelated images.</t>
          </li>
          <li>
            <t>Version Control: Similar to Git LFS but with content-aware chunking that enables sharing across different files, not just versions of the same file.</t>
          </li>
          <li>
            <t>Content Distribution: The reconstruction-based model enables efficient range queries and partial downloads of large files.</t>
          </li>
        </ul>
      </section>
    </section>
    <section anchor="terminology">
      <name>Terminology</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>
      <?line -18?>

<t>Throughout this document, the following terms apply:</t>
      <table>
        <thead>
          <tr>
            <th align="left">Term</th>
            <th align="left">Definition</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="left">Algorithm Suite</td>
            <td align="left">A specification of the cryptographic hash function and content-defined chunking algorithm used by an XET deployment. All participants in an XET system <bcp14>MUST</bcp14> use the same algorithm suite for interoperability.</td>
          </tr>
          <tr>
            <td align="left">Chunk</td>
            <td align="left">A variable-sized unit of data derived from a file using content-defined chunking. Chunks are the fundamental unit of deduplication in XET.</td>
          </tr>
          <tr>
            <td align="left">Chunk Hash</td>
            <td align="left">A 32-byte cryptographic hash that uniquely identifies a chunk based on its content.</td>
          </tr>
          <tr>
            <td align="left">Xorb</td>
            <td align="left">A container object that aggregates multiple compressed chunks for efficient storage and transfer. The name derives from “XET orb.”</td>
          </tr>
          <tr>
            <td align="left">Xorb Hash</td>
            <td align="left">A 32-byte cryptographic hash computed from the chunk hashes within a xorb using a Merkle tree construction.</td>
          </tr>
          <tr>
            <td align="left">File Hash</td>
            <td align="left">A 32-byte cryptographic hash that uniquely identifies a file based on its chunk composition.</td>
          </tr>
          <tr>
            <td align="left">Shard</td>
            <td align="left">A binary metadata structure that describes file reconstructions and xorb contents, used for registering uploads and enabling deduplication.</td>
          </tr>
          <tr>
            <td align="left">Term</td>
            <td align="left">A reference to a contiguous range of chunks within a specific xorb, used to describe how to reconstruct a file.</td>
          </tr>
          <tr>
            <td align="left">File Reconstruction</td>
            <td align="left">An ordered list of terms that describes how to reassemble a file from chunks stored in xorbs.</td>
          </tr>
          <tr>
            <td align="left">Content-Defined Chunking (CDC)</td>
            <td align="left">An algorithm that determines chunk boundaries based on file content rather than fixed offsets, enabling stable boundaries across file modifications.</td>
          </tr>
          <tr>
            <td align="left">Content-Addressable Storage (CAS)</td>
            <td align="left">A storage system where objects are addressed by cryptographic hashes of their content rather than by location or name.</td>
          </tr>
          <tr>
            <td align="left">Global Deduplication</td>
            <td align="left">The process of identifying chunks that already exist in the storage system to avoid redundant uploads.</td>
          </tr>
        </tbody>
      </table>
      <section anchor="notational-conventions">
        <name>Notational Conventions</name>
        <t>All multi-byte integers in binary formats (xorb headers, shard structures) use little-endian byte order unless otherwise specified.</t>
        <t>Hash values are 32 bytes (256 bits).
When serialized, they are stored as raw bytes.
When displayed as strings, they use a specific byte-swapped hexadecimal format (see <xref target="hash-string-format"/>).</t>
        <t>Range specifications use two conventions:</t>
        <ul spacing="normal">
          <li>
            <t>Index ranges (chunk indices): exclusive end, <tt>[start, end)</tt>. Example: <tt>{"start": 0, "end": 4}</tt> means indices 0, 1, 2, 3.</t>
          </li>
          <li>
            <t>Byte ranges (<tt>url_range</tt>, HTTP <tt>Range</tt> header): inclusive end, <tt>[start, end]</tt>. Example: <tt>{"start": 0, "end": 999}</tt> means bytes 0 through 999.</t>
          </li>
        </ul>
        <t>The <tt>url_range</tt> field uses inclusive semantics so it can be used directly in HTTP <tt>Range</tt> headers without modification.</t>
        <section anchor="pseudo-code-conventions">
          <name>Pseudo-Code Conventions</name>
          <t>Pseudo-code in this document uses the following conventions:</t>
          <ul spacing="normal">
            <li>
              <t><tt>for i = a to b:</tt> iterates with <tt>i</tt> taking values <tt>a, a+1, ..., b</tt> (inclusive)</t>
            </li>
            <li>
              <t><tt>for each x in list:</tt> iterates over each element in <tt>list</tt></t>
            </li>
            <li>
              <t><tt>//</tt> denotes integer division (truncating toward zero)</t>
            </li>
            <li>
              <t><tt>%</tt> denotes the modulo operator</t>
            </li>
            <li>
              <t><tt>array[start:end]</tt> slices from index <tt>start</tt> (inclusive) to <tt>end</tt> (exclusive)</t>
            </li>
            <li>
              <t><tt>+</tt> on arrays denotes concatenation</t>
            </li>
          </ul>
        </section>
      </section>
    </section>
    <section anchor="protocol-overview">
      <name>Protocol Overview</name>
      <t>XET operates as a client-server protocol.
Clients perform content-defined chunking locally, query for deduplication opportunities, form xorbs from new chunks, and upload both xorbs and shards to the server.
The CAS server provides APIs for reconstruction queries, global deduplication, and persistent storage.</t>
      <section anchor="upload-flow">
        <name>Upload Flow</name>
        <t>The upload process transforms files into content-addressed storage:</t>
        <ol spacing="normal" type="1"><li>
            <t>Chunking: Split files into variable-sized chunks using content-defined chunking (see <xref target="content-defined-chunking"/>).</t>
          </li>
          <li>
            <t>Deduplication: Query for existing chunks to avoid redundant uploads (see <xref target="deduplication"/>).</t>
          </li>
          <li>
            <t>Xorb Formation: Group new chunks into xorbs, applying compression (see <xref target="xorb-format"/>).</t>
          </li>
          <li>
            <t>Xorb Upload: Upload serialized xorbs to the CAS server.</t>
          </li>
          <li>
            <t>Shard Formation: Create shard metadata describing file reconstructions.</t>
          </li>
          <li>
            <t>Shard Upload: Upload the shard to register files in the system.</t>
          </li>
        </ol>
      </section>
      <section anchor="download-flow">
        <name>Download Flow</name>
        <t>The download process reconstructs files from stored chunks:</t>
        <ol spacing="normal" type="1"><li>
            <t>Reconstruction Query: Request reconstruction information for a file hash.</t>
          </li>
          <li>
            <t>Term Processing: Parse the ordered list of terms describing the file.</t>
          </li>
          <li>
            <t>Data Fetching: Download required xorb ranges using provided URLs.</t>
          </li>
          <li>
            <t>Chunk Extraction: Deserialize and decompress chunks from xorb data.</t>
          </li>
          <li>
            <t>File Assembly: Concatenate chunks in term order to reconstruct the file.</t>
          </li>
        </ol>
      </section>
    </section>
    <section anchor="algorithm-suites">
      <name>Algorithm Suites</name>
      <t>XET is designed as a generic framework where the specific chunking algorithm and cryptographic hash function are parameters defined by an algorithm suite.
This enables future algorithm agility while maintaining full backward compatibility within a deployment.</t>
      <section anchor="suite-definition">
        <name>Suite Definition</name>
        <t>An algorithm suite specifies:</t>
        <ol spacing="normal" type="1"><li>
            <t>Content-Defined Chunking Algorithm: The rolling hash function and boundary detection logic used to split files into chunks.</t>
          </li>
          <li>
            <t>Cryptographic Hash Function: The hash algorithm used for all content addressing (chunk hashes, xorb hashes, file hashes, verification hashes).</t>
          </li>
          <li>
            <t>Keying Material: Domain separation keys for the hash function.</t>
          </li>
          <li>
            <t>Algorithm Parameters: Chunk size bounds, mask values, lookup tables, and other constants.</t>
          </li>
        </ol>
      </section>
      <section anchor="suite-requirements">
        <name>Suite Requirements</name>
        <t>Any conforming algorithm suite <bcp14>MUST</bcp14> satisfy:</t>
        <ul spacing="normal">
          <li>
            <t>Determinism: Identical inputs <bcp14>MUST</bcp14> produce identical outputs across all implementations.</t>
          </li>
          <li>
            <t>Collision Resistance: The hash function <bcp14>MUST</bcp14> provide at least 128 bits of collision resistance.</t>
          </li>
          <li>
            <t>Preimage Resistance: The hash function <bcp14>MUST</bcp14> provide at least 128 bits of preimage resistance.</t>
          </li>
          <li>
            <t>Keyed Mode: The hash function <bcp14>MUST</bcp14> support keyed operation for domain separation.</t>
          </li>
        </ul>
      </section>
      <section anchor="suite-negotiation">
        <name>Suite Negotiation</name>
        <t>The algorithm suite used by an XET deployment is determined out-of-band, typically by the CAS server configuration.
All clients interacting with a given server <bcp14>MUST</bcp14> use the same suite.
Binary formats (xorbs, shards) do not contain suite identifiers; the suite is determined implicitly by the deployment context.</t>
      </section>
      <section anchor="defined-suites">
        <name>Defined Suites</name>
        <t>This specification defines one algorithm suite:</t>
        <ul spacing="normal">
          <li>
            <t><tt>XET-GEARHASH-BLAKE3</tt>: Uses <tt>Gearhash</tt> for content-defined chunking and <tt>BLAKE3</tt> for all cryptographic hashing. This is the default and currently only defined suite.</t>
          </li>
        </ul>
        <t>Future specifications <bcp14>MAY</bcp14> define additional suites with different algorithms.</t>
      </section>
    </section>
    <section anchor="content-defined-chunking">
      <name>Content-Defined Chunking</name>
      <t>Content-defined chunking (CDC) splits files into variable-sized chunks based on content rather than fixed offsets.
This produces deterministic chunk boundaries that remain stable across file modifications, enabling efficient deduplication.</t>
      <t>This section describes the chunking algorithm for the <tt>XET-GEARHASH-BLAKE3</tt> suite.
Other algorithm suites <bcp14>MAY</bcp14> define different chunking algorithms with different parameters.</t>
      <section anchor="gearhash-algorithm">
        <name>Gearhash Algorithm</name>
        <t>The <tt>XET-GEARHASH-BLAKE3</tt> suite uses a <tt>Gearhash</tt>-based rolling hash algorithm <xref target="GEARHASH"/>.
<tt>Gearhash</tt> maintains a 64-bit state that is updated with each input byte using a lookup table, providing fast and deterministic boundary detection.</t>
      </section>
      <section anchor="algorithm-parameters">
        <name>Algorithm Parameters</name>
        <t>The following constants define the chunking behavior for the <tt>XET-GEARHASH-BLAKE3</tt> suite:</t>
        <artwork><![CDATA[
TARGET_CHUNK_SIZE  = 65536      # 64 KiB (2^16 bytes)
MIN_CHUNK_SIZE     = 8192       # 8 KiB (TARGET / 8)
MAX_CHUNK_SIZE     = 131072     # 128 KiB (TARGET * 2)
MASK               = 0xFFFF000000000000  # 16 one-bits
]]></artwork>
        <t>The <tt>Gearhash</tt> algorithm uses a lookup table of 256 64-bit constants.
Implementations of the <tt>XET-GEARHASH-BLAKE3</tt> suite <bcp14>MUST</bcp14> use the table defined in <xref target="GEARHASH"/> (see <xref target="gearhash-table"/> for the complete lookup table).</t>
      </section>
      <section anchor="algorithm-description">
        <name>Algorithm Description</name>
        <t>The algorithm maintains a 64-bit rolling hash value and processes input bytes sequentially:</t>
        <artwork><![CDATA[
function chunk_file(data):
    h = 0                    # 64-bit rolling hash
    start_offset = 0         # Start of current chunk
    chunks = []
    n = length(data)

    for i = 0 to n - 1:      # Inclusive range [0, n-1]
        b = data[i]
        h = ((h << 1) + TABLE[b]) & 0xFFFFFFFFFFFFFFFF  # 64-bit wrap

        chunk_size = i - start_offset + 1

        if chunk_size < MIN_CHUNK_SIZE:
            continue

        if chunk_size >= MAX_CHUNK_SIZE:
            chunks.append(data[start_offset : i + 1])
            start_offset = i + 1
            h = 0
            continue

        if (h & MASK) == 0:
            chunks.append(data[start_offset : i + 1])
            start_offset = i + 1
            h = 0

    if start_offset < n:
        chunks.append(data[start_offset : n])

    return chunks
]]></artwork>
      </section>
      <section anchor="boundary-rules">
        <name>Boundary Rules</name>
        <t>The following rules govern chunk boundary placement:</t>
        <ol spacing="normal" type="1"><li>
            <t>Boundaries <bcp14>MUST NOT</bcp14> be placed before <tt>MIN_CHUNK_SIZE</tt> bytes have been processed in the current chunk.</t>
          </li>
          <li>
            <t>Boundaries <bcp14>MUST</bcp14> be forced when <tt>MAX_CHUNK_SIZE</tt> bytes have been processed, regardless of hash value.</t>
          </li>
          <li>
            <t>Between minimum and maximum sizes, boundaries are placed when <tt>(h &amp; MASK) == 0</tt>.</t>
          </li>
          <li>
            <t>The final chunk <bcp14>MAY</bcp14> be smaller than <tt>MIN_CHUNK_SIZE</tt> if it represents the end of the file.</t>
          </li>
          <li>
            <t>Files smaller than <tt>MIN_CHUNK_SIZE</tt> produce a single chunk.</t>
          </li>
        </ol>
      </section>
      <section anchor="determinism-requirements">
        <name>Determinism Requirements</name>
        <t>Implementations <bcp14>MUST</bcp14> produce identical chunk boundaries for identical input data.
For the <tt>XET-GEARHASH-BLAKE3</tt> suite, this requires:</t>
        <ul spacing="normal">
          <li>
            <t>Using the exact lookup table values from <xref target="gearhash-table"/></t>
          </li>
          <li>
            <t>Using 64-bit wrapping arithmetic for hash updates</t>
          </li>
          <li>
            <t>Processing bytes in sequential order</t>
          </li>
          <li>
            <t>Applying boundary rules consistently</t>
          </li>
        </ul>
        <t>Other algorithm suites <bcp14>MUST</bcp14> specify their own determinism requirements.</t>
      </section>
      <section anchor="performance-optimization">
        <name>Performance Optimization</name>
        <t>Implementations <bcp14>MAY</bcp14> skip hash computation for the first <tt>MIN_CHUNK_SIZE - 64 - 1</tt> bytes of each chunk, as boundary tests are not performed in this region.</t>
        <t>This optimization does not affect output correctness because the <tt>Gearhash</tt> window is 64 bytes, ensuring the hash state is fully populated by the time boundary tests begin.</t>
      </section>
    </section>
    <section anchor="hashing-methods">
      <name>Hashing Methods</name>
      <t>XET uses cryptographic hashing for content addressing, integrity verification, and deduplication.
The specific hash function is determined by the algorithm suite.
All hashes are 32 bytes (256 bits) in length.</t>
      <t>This section describes the hashing methods for the <tt>XET-GEARHASH-BLAKE3</tt> suite, which uses <tt>BLAKE3</tt> keyed hashing <xref target="BLAKE3"/> for all cryptographic hash computations.
Different key values provide domain separation between hash types.</t>
      <section anchor="chunk-hashes">
        <name>Chunk Hashes</name>
        <t>Chunk hashes uniquely identify individual chunks based on their content.
The algorithm suite determines how chunk hashes are computed.</t>
        <t>For the <tt>XET-GEARHASH-BLAKE3</tt> suite, chunk hashes use <tt>BLAKE3</tt> keyed hash with <tt>DATA_KEY</tt> as the key:</t>
        <artwork><![CDATA[
DATA_KEY = {
  0x66, 0x97, 0xf5, 0x77, 0x5b, 0x95, 0x50, 0xde,
  0x31, 0x35, 0xcb, 0xac, 0xa5, 0x97, 0x18, 0x1c,
  0x9d, 0xe4, 0x21, 0x10, 0x9b, 0xeb, 0x2b, 0x58,
  0xb4, 0xd0, 0xb0, 0x4b, 0x93, 0xad, 0xf2, 0x29
}
]]></artwork>
        <artwork><![CDATA[
function compute_chunk_hash(chunk_data):
    return blake3_keyed_hash(DATA_KEY, chunk_data)
]]></artwork>
      </section>
      <section anchor="xorb-hashes">
        <name>Xorb Hashes</name>
        <t>Xorb hashes identify xorbs based on their constituent chunks.
The hash is computed using a Merkle tree construction where leaf nodes are chunk hashes.
The Merkle tree construction is defined separately from the hash function.</t>
        <section anchor="internal-node-hash-function">
          <name>Internal Node Hash Function</name>
          <t>Internal node hashes combine child hashes with their sizes.
The hash function is determined by the algorithm suite.</t>
          <t>For the <tt>XET-GEARHASH-BLAKE3</tt> suite, internal node hashes use <tt>BLAKE3</tt> keyed hash with <tt>INTERNAL_NODE_KEY</tt> as the key:</t>
          <artwork><![CDATA[
INTERNAL_NODE_KEY = {
  0x01, 0x7e, 0xc5, 0xc7, 0xa5, 0x47, 0x29, 0x96,
  0xfd, 0x94, 0x66, 0x66, 0xb4, 0x8a, 0x02, 0xe6,
  0x5d, 0xdd, 0x53, 0x6f, 0x37, 0xc7, 0x6d, 0xd2,
  0xf8, 0x63, 0x52, 0xe6, 0x4a, 0x53, 0x71, 0x3f
}
]]></artwork>
          <t>The input to the hash function is a string formed by concatenating lines for each child:</t>
          <artwork><![CDATA[
{hash_hex} : {size}\n
]]></artwork>
          <t>Where:</t>
          <ul spacing="normal">
            <li>
              <t><tt>{hash_hex}</tt> is the 64-character lowercase hexadecimal representation of the child hash as defined in <xref target="hash-string-format"/></t>
            </li>
            <li>
              <t><tt>{size}</tt> is the decimal representation of the child’s byte size</t>
            </li>
            <li>
              <t>Lines are separated by newline characters (<tt>\n</tt>)</t>
            </li>
          </ul>
        </section>
        <section anchor="merkle-tree">
          <name>Merkle Tree Construction</name>
          <t>XET uses an aggregated hash tree construction with variable fan-out, not a traditional binary Merkle tree.
This algorithm iteratively collapses a list of (hash, size) pairs until a single root hash remains.</t>
          <section anchor="algorithm-parameters-1">
            <name>Algorithm Parameters</name>
            <artwork><![CDATA[
MEAN_BRANCHING_FACTOR = 4
MIN_CHILDREN = 2
MAX_CHILDREN = 2 * MEAN_BRANCHING_FACTOR + 1  # 9
]]></artwork>
          </section>
          <section anchor="cut-point-determination">
            <name>Cut Point Determination</name>
            <t>The tree structure is determined by the hash values themselves.
A cut point occurs when:</t>
            <ol spacing="normal" type="1"><li>
                <t>At least 3 children have been accumulated AND the current hash modulo MEAN_BRANCHING_FACTOR equals zero, OR</t>
              </li>
              <li>
                <t>The maximum number of children (9) has been reached, OR</t>
              </li>
              <li>
                <t>The end of the input list is reached</t>
              </li>
            </ol>
            <t>Note: When the input has 2 or fewer hashes, all are merged together.
This ensures each internal node has at least 2 children.</t>
            <artwork><![CDATA[
function next_merge_cut(hashes):
    # hashes is a list of (hash, size) pairs
    # Returns the number of entries to merge (cut point)
    n = length(hashes)
    if n <= 2:
        return n

    end = min(MAX_CHILDREN, n)

    # Check indices 2 through end-1 (0-based indexing)
    # Minimum merge is 3 children when input has more than 2 hashes
    for i = 2 to end - 1:
        h = hashes[i].hash
        # Interpret last 8 bytes of hash as little-endian 64-bit unsigned int
        hash_value = u64_le(h[24:32])
        if hash_value % MEAN_BRANCHING_FACTOR == 0:
            return i + 1  # Cut after element i (include i+1 elements)

    return end
]]></artwork>
          </section>
          <section anchor="merging-hash-sequences">
            <name>Merging Hash Sequences</name>
            <artwork><![CDATA[
function merged_hash_of_sequence(hash_pairs):
    # hash_pairs is a list of (hash, size) pairs
    buffer = ""
    total_size = 0

    for each (h, s) in hash_pairs:
        buffer += hash_to_string(h) + " : " + decimal_string(s) + "\n"
        total_size += s

    new_hash = blake3_keyed_hash(INTERNAL_NODE_KEY, utf8_encode(buffer))
    return (new_hash, total_size)
]]></artwork>
            <t>This produces lines like:</t>
            <artwork><![CDATA[
cfc5d07f6f03c29bbf424132963fe08d19a37d5757aaf520bf08119f05cd56d6 : 100
]]></artwork>
            <t>Each line contains:</t>
            <ul spacing="normal">
              <li>
                <t>The hash as a fixed-length 64-character lowercase hexadecimal string</t>
              </li>
              <li>
                <t>A space, colon, space (<tt> : </tt>)</t>
              </li>
              <li>
                <t>The size as a decimal integer</t>
              </li>
              <li>
                <t>A newline character (<tt>\n</tt>)</t>
              </li>
            </ul>
          </section>
          <section anchor="root-computation">
            <name>Root Computation</name>
            <artwork><![CDATA[
function compute_merkle_root(entries):
    # entries is a list of (hash, size) pairs
    if length(entries) == 0:
        return ZERO_HASH  # 32 zero bytes

    hv = entries  # Working copy

    while length(hv) > 1:
        write_idx = 0
        read_idx = 0

        while read_idx < length(hv):
            cut = read_idx + next_merge_cut(hv[read_idx:])
            hv[write_idx] = merged_hash_of_sequence(hv[read_idx:cut])
            write_idx += 1
            read_idx = cut

        hv = hv[0:write_idx]

    return hv[0].hash
]]></artwork>
            <t>Where <tt>ZERO_HASH</tt> is 32 bytes of zeros, and <tt>hv[start:end]</tt> denotes slicing elements from index <tt>start</tt> (inclusive) to <tt>end</tt> (exclusive).</t>
          </section>
        </section>
        <section anchor="xorb-hash-computation">
          <name>Xorb Hash Computation</name>
          <t>The xorb hash is the root of a Merkle tree built from chunk hashes:</t>
          <artwork><![CDATA[
function compute_xorb_hash(chunk_hashes, chunk_sizes):
    n = length(chunk_hashes)
    entries = []
    for i = 0 to n - 1:
        entries.append((chunk_hashes[i], chunk_sizes[i]))
    return compute_merkle_root(entries)
]]></artwork>
        </section>
      </section>
      <section anchor="file-hashes">
        <name>File Hashes</name>
        <t>File hashes identify files based on their complete chunk composition.
The computation is similar to xorb hashes, but with an additional final keyed hash step for domain separation.</t>
        <t>For the <tt>XET-GEARHASH-BLAKE3</tt> suite, file hashes use an all-zero key (<tt>ZERO_KEY</tt>) for the final hash:</t>
        <artwork><![CDATA[
ZERO_KEY = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}
]]></artwork>
        <artwork><![CDATA[
function compute_file_hash(chunk_hashes, chunk_sizes):
    n = length(chunk_hashes)
    entries = []
    for i = 0 to n - 1:
        entries.append((chunk_hashes[i], chunk_sizes[i]))
    merkle_root = compute_merkle_root(entries)
    return blake3_keyed_hash(ZERO_KEY, merkle_root)
]]></artwork>
        <t>For empty files (zero bytes), there are no chunks, so <tt>compute_merkle_root([])</tt> returns <tt>ZERO_HASH</tt> (32 zero bytes).
The file hash is therefore <tt>blake3_keyed_hash(ZERO_KEY, ZERO_HASH)</tt>.</t>
      </section>
      <section anchor="verification-hashes">
        <name>Term Verification Hashes</name>
        <t>Term verification hashes are used in shards to prove that the uploader possesses the actual file data, not just metadata.
The hash function is determined by the algorithm suite.</t>
        <t>For the <tt>XET-GEARHASH-BLAKE3</tt> suite, verification hashes use <tt>BLAKE3</tt> keyed hash with <tt>VERIFICATION_KEY</tt> as the key:</t>
        <artwork><![CDATA[
VERIFICATION_KEY = {
  0x7f, 0x18, 0x57, 0xd6, 0xce, 0x56, 0xed, 0x66,
  0x12, 0x7f, 0xf9, 0x13, 0xe7, 0xa5, 0xc3, 0xf3,
  0xa4, 0xcd, 0x26, 0xd5, 0xb5, 0xdb, 0x49, 0xe6,
  0x41, 0x24, 0x98, 0x7f, 0x28, 0xfb, 0x94, 0xc3
}
]]></artwork>
        <t>The input is the raw concatenation of chunk hashes (not hex-encoded) for the term’s chunk range:</t>
        <artwork><![CDATA[
function compute_verification_hash(chunk_hashes, start_index, end_index):
    # Range is [start_index, end_index) - end is exclusive
    buffer = empty_byte_array()
    for i = start_index to end_index - 1:
        buffer += chunk_hashes[i]  # 32 bytes each
    return blake3_keyed_hash(VERIFICATION_KEY, buffer)
]]></artwork>
      </section>
      <section anchor="hash-string-format">
        <name>Hash String Representation</name>
        <t>When representing hashes as strings (e.g., in API paths), a specific byte reordering is applied before hexadecimal encoding.</t>
        <section anchor="conversion-procedure">
          <name>Conversion Procedure</name>
          <t>The 32-byte hash is interpreted as four little-endian 64-bit unsigned values, and each value is printed as 16 hexadecimal digits:</t>
          <ol spacing="normal" type="1"><li>
              <t>Divide the 32-byte hash into four 8-byte segments</t>
            </li>
            <li>
              <t>Interpret each segment as a little-endian 64-bit unsigned value</t>
            </li>
            <li>
              <t>Format each value as a zero-padded 16-character lowercase hexadecimal string</t>
            </li>
            <li>
              <t>Concatenate the four strings (64 characters total)</t>
            </li>
          </ol>
          <artwork><![CDATA[
function hash_to_string(hash):
    out = ""
    for segment = 0 to 3:
        offset = segment * 8
        value = u64_le(hash[offset : offset + 8])
        out += hex16(value)    # 16-digit lowercase hex
    return out

function string_to_hash(hex_string):
    hash = empty_byte_array()
    for segment = 0 to 3:
        start = segment * 16
        value = parse_hex_u64(hex_string[start : start + 16])
        hash += u64_le_bytes(value)
    return hash
]]></artwork>
          <t>Where:</t>
          <ul spacing="normal">
            <li>
              <t><tt>u64_le(bytes)</tt> interprets 8 bytes as a little-endian 64-bit unsigned integer</t>
            </li>
            <li>
              <t><tt>u64_le_bytes(value)</tt> converts a 64-bit unsigned integer to 8 little-endian bytes</t>
            </li>
            <li>
              <t><tt>hex16(value)</tt> formats a 64-bit value as a 16-character lowercase hexadecimal string</t>
            </li>
            <li>
              <t><tt>parse_hex_u64(str)</tt> parses a 16-character hexadecimal string as a 64-bit unsigned integer</t>
            </li>
          </ul>
        </section>
        <section anchor="example">
          <name>Example</name>
          <artwork><![CDATA[
Original hash bytes (indices 0-31):
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]

Reordered bytes:
[7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8,
 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24]

String representation:
07060504030201000f0e0d0c0b0a090817161514131211101f1e1d1c1b1a1918
]]></artwork>
        </section>
      </section>
    </section>
    <section anchor="xorb-format">
      <name>Xorb Format</name>
      <t>A xorb is a container that aggregates multiple compressed chunks for efficient storage and transfer.
Xorbs are identified by their xorb hash (see <xref target="xorb-hashes"/>).</t>
      <section anchor="size-constraints">
        <name>Size Constraints</name>
        <artwork><![CDATA[
MAX_XORB_SIZE   = 67108864  # 64 MiB maximum serialized size
MAX_XORB_CHUNKS = 8192      # Maximum chunks per xorb
]]></artwork>
        <t>Implementations <bcp14>MUST NOT</bcp14> exceed either limit.
When collecting chunks:</t>
        <ol spacing="normal" type="1"><li>
            <t>Stop if adding the next chunk would exceed <tt>MAX_XORB_SIZE</tt></t>
          </li>
          <li>
            <t>Stop if the chunk count would exceed <tt>MAX_XORB_CHUNKS</tt></t>
          </li>
          <li>
            <t>Target approximately 1,024 chunks per xorb for typical workloads</t>
          </li>
        </ol>
      </section>
      <section anchor="binary-format">
        <name>Binary Format</name>
        <t>Serialized xorbs have a footer so readers can locate metadata by seeking from the end:</t>
        <artwork><![CDATA[
+-------------------------------------------------------------+
|                 Chunk Data Region (variable)                |
|   [chunk header + compressed bytes repeated per chunk]      |
+-------------------------------------------------------------+
|                 CasObjectInfo Footer (variable)             |
+-------------------------------------------------------------+
|     Info Length (32-bit unsigned LE, footer length only)    |
+-------------------------------------------------------------+
]]></artwork>
        <t>The final 4-byte little-endian integer stores the length of the <tt>CasObjectInfo</tt> block immediately preceding it (the length does not include the 4-byte length field itself).</t>
        <t>The chunk data region consists of consecutive chunk entries, each containing an 8-byte header followed by the compressed chunk data.</t>
      </section>
      <section anchor="chunk-header-format">
        <name>Chunk Header Format</name>
        <t>Each chunk header is 8 bytes with the following layout:</t>
        <table>
          <thead>
            <tr>
              <th align="left">Offset</th>
              <th align="left">Size</th>
              <th align="left">Field</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td align="left">0</td>
              <td align="left">1</td>
              <td align="left">Version (must be 0)</td>
            </tr>
            <tr>
              <td align="left">1</td>
              <td align="left">3</td>
              <td align="left">Compressed Size (little-endian, bytes)</td>
            </tr>
            <tr>
              <td align="left">4</td>
              <td align="left">1</td>
              <td align="left">Compression Type</td>
            </tr>
            <tr>
              <td align="left">5</td>
              <td align="left">3</td>
              <td align="left">Uncompressed Size (little-endian, bytes)</td>
            </tr>
          </tbody>
        </table>
        <section anchor="version-field">
          <name>Version Field</name>
          <t>The version field <bcp14>MUST</bcp14> be <tt>0</tt> for this specification.
Implementations <bcp14>MUST</bcp14> reject chunks with unknown version values.</t>
        </section>
        <section anchor="size-fields">
          <name>Size Fields</name>
          <t>Both size fields use 3-byte little-endian encoding, supporting values up to 16,777,215 bytes.
Given the maximum chunk size of 128 KiB, this provides ample range.</t>
          <t>Implementations <bcp14>MUST</bcp14> validate size fields before allocating buffers or invoking decompression:</t>
          <ul spacing="normal">
            <li>
              <t><tt>uncompressed_size</tt> <bcp14>MUST</bcp14> be greater than zero and <bcp14>MUST NOT</bcp14> exceed <tt>MAX_CHUNK_SIZE</tt> (128 KiB). Chunks that declare larger sizes <bcp14>MUST</bcp14> be rejected and the containing xorb considered invalid.</t>
            </li>
            <li>
              <t><tt>compressed_size</tt> <bcp14>MUST</bcp14> be greater than zero and <bcp14>MUST NOT</bcp14> exceed the lesser of <tt>MAX_CHUNK_SIZE</tt> and the remaining bytes in the serialized xorb payload. Oversize or truncated compressed payloads <bcp14>MUST</bcp14> cause the xorb to be rejected.</t>
            </li>
          </ul>
        </section>
        <section anchor="compression-type">
          <name>Compression Type</name>
          <table>
            <thead>
              <tr>
                <th align="left">Value</th>
                <th align="left">Name</th>
                <th align="left">Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">0</td>
                <td align="left">
                  <tt>None</tt></td>
                <td align="left">No compression; data stored as-is</td>
              </tr>
              <tr>
                <td align="left">1</td>
                <td align="left">
                  <tt>LZ4</tt></td>
                <td align="left">
                  <tt>LZ4</tt> Frame format compression</td>
              </tr>
              <tr>
                <td align="left">2</td>
                <td align="left">
                  <tt>ByteGrouping4LZ4</tt></td>
                <td align="left">Byte grouping preprocessing followed by <tt>LZ4</tt></td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="compression-schemes">
        <name>Compression Schemes</name>
        <section anchor="none-type-0">
          <name><tt>None</tt> (Type 0)</name>
          <t>Data is stored without modification.
Used when compression would increase size or for already-compressed data.</t>
        </section>
        <section anchor="lz4-type-1">
          <name><tt>LZ4</tt> (Type 1)</name>
          <t><tt>LZ4</tt> Frame format compression <xref target="LZ4"/> (not <tt>LZ4</tt> block format).
Each compressed chunk is a complete <tt>LZ4</tt> frame.
This is the default compression scheme for most data.</t>
        </section>
        <section anchor="bytegrouping4lz4-type-2">
          <name><tt>ByteGrouping4LZ4</tt> (Type 2)</name>
          <t>A two-stage compression optimized for structured data (e.g., floating-point arrays):</t>
          <ol spacing="normal" type="1"><li>
              <t>Byte Grouping Phase: Reorganize bytes by position within 4-byte groups</t>
            </li>
            <li>
              <t><tt>LZ4</tt> Compression: Apply <tt>LZ4</tt> to the reorganized data</t>
            </li>
          </ol>
          <t>Byte grouping transformation:</t>
          <artwork><![CDATA[
Original:  [A0 A1 A2 A3 | B0 B1 B2 B3 | C0 C1 C2 C3 | ...]
Grouped:   [A0 B0 C0 ... | A1 B1 C1 ... | A2 B2 C2 ... | A3 B3 C3 ...]
]]></artwork>
          <artwork><![CDATA[
function byte_group_4(data):
    n = length(data)
    groups = [[], [], [], []]

    for i = 0 to n - 1:
        groups[i % 4].append(data[i])

    return groups[0] + groups[1] + groups[2] + groups[3]

function byte_ungroup_4(grouped_data, original_length):
    n = original_length
    base_size = n // 4         # Integer division
    remainder = n % 4

    # Group sizes: first 'remainder' groups get base_size + 1
    sizes = []
    for i = 0 to 3:
        if i < remainder:
            sizes.append(base_size + 1)
        else:
            sizes.append(base_size)

    # Extract groups from grouped_data
    groups = []
    offset = 0
    for i = 0 to 3:
        groups.append(grouped_data[offset : offset + sizes[i]])
        offset += sizes[i]

    # Interleave back to original order
    data = []
    for i = 0 to n - 1:
        group_idx = i % 4
        pos_in_group = i // 4  # Integer division
        data.append(groups[group_idx][pos_in_group])

    return data
]]></artwork>
          <t>When the data length is not a multiple of 4, the remainder bytes are distributed to the first groups.
For example, with 10 bytes the group sizes are 3, 3, 2, 2 (first two groups get the extra bytes).</t>
        </section>
        <section anchor="compression-selection">
          <name>Compression Selection</name>
          <t>Implementations <bcp14>MAY</bcp14> use any strategy to select compression schemes.
If compression increases size, implementations <bcp14>SHOULD</bcp14> use compression type <tt>0</tt> (<tt>None</tt>).</t>
          <t><tt>ByteGrouping4LZ4</tt> (Type 2) is typically beneficial for structured numerical data such as <tt>float32</tt> or <tt>float16</tt> tensors, where bytes at the same position within 4-byte groups tend to be similar.</t>
        </section>
      </section>
      <section anchor="casobjectinfo-footer">
        <name><tt>CasObjectInfo</tt> Footer</name>
        <t>The metadata footer sits immediately before the 4-byte length trailer.
Implementations <bcp14>MUST</bcp14> serialize fields in this exact order and reject unknown idents or versions.</t>
        <section anchor="main-header">
          <name>Main Header</name>
          <ul spacing="normal">
            <li>
              <t>Ident: <tt>"XETBLOB"</tt> (7 ASCII bytes)</t>
            </li>
            <li>
              <t>Version: 8-bit unsigned, <bcp14>MUST</bcp14> be <tt>1</tt></t>
            </li>
            <li>
              <t>Xorb hash: 32-byte Merkle hash from <xref target="xorb-hashes"/></t>
            </li>
          </ul>
        </section>
        <section anchor="hash-section">
          <name>Hash Section</name>
          <ul spacing="normal">
            <li>
              <t>Ident: <tt>"XBLBHSH"</tt> (7 bytes)</t>
            </li>
            <li>
              <t>Hashes version: 8-bit unsigned, <bcp14>MUST</bcp14> be <tt>0</tt></t>
            </li>
            <li>
              <t><tt>num_chunks</tt>: 32-bit unsigned</t>
            </li>
            <li>
              <t>Chunk hashes: 32 bytes each, in chunk order</t>
            </li>
          </ul>
        </section>
        <section anchor="boundary-section">
          <name>Boundary Section</name>
          <ul spacing="normal">
            <li>
              <t>Ident: <tt>"XBLBBND"</tt> (7 bytes)</t>
            </li>
            <li>
              <t>Boundaries version: 8-bit unsigned, <bcp14>MUST</bcp14> be <tt>1</tt></t>
            </li>
            <li>
              <t><tt>num_chunks</tt>: 32-bit unsigned</t>
            </li>
            <li>
              <t>Chunk boundary offsets: Array of <tt>num_chunks</tt> 32-bit unsigned values.
Each value is the end offset (in bytes) of the corresponding chunk in the serialized chunk data region, including headers.
Chunk 0 starts at offset 0; chunk <tt>i</tt> starts at <tt>chunk_boundary_offsets[i-1]</tt>.</t>
            </li>
            <li>
              <t>Unpacked chunk offsets: Array of <tt>num_chunks</tt> 32-bit unsigned values.
Each value is the end offset of the corresponding chunk in the concatenated uncompressed stream.</t>
            </li>
          </ul>
        </section>
        <section anchor="trailer">
          <name>Trailer</name>
          <ul spacing="normal">
            <li>
              <t><tt>num_chunks</tt>: 32-bit unsigned (repeated for convenience)</t>
            </li>
            <li>
              <t>Hashes section offset from end: 32-bit unsigned byte offset from the end of the footer to the start of the hash section</t>
            </li>
            <li>
              <t>Boundary section offset from end: 32-bit unsigned byte offset from the end of the footer to the start of the boundary section</t>
            </li>
            <li>
              <t>Reserved: 16 bytes, zero</t>
            </li>
          </ul>
          <t>The 4-byte length trailer that follows the footer stores <tt>info_length</tt> (little-endian 32-bit unsigned) for the <tt>CasObjectInfo</tt> block only.
This length field is not counted inside the footer itself.</t>
        </section>
      </section>
    </section>
    <section anchor="file-reconstruction">
      <name>File Reconstruction</name>
      <t>A file reconstruction is an ordered list of terms that describes how to reassemble a file from chunks stored in xorbs.</t>
      <section anchor="term-structure">
        <name>Term Structure</name>
        <t>Each term specifies:</t>
        <ul spacing="normal">
          <li>
            <t>Xorb Hash: Identifies the xorb containing the chunks</t>
          </li>
          <li>
            <t>Chunk Range: Start (inclusive) and end (exclusive) indices within the xorb</t>
          </li>
          <li>
            <t>Unpacked Length: Expected byte count after decompression (for validation)</t>
          </li>
        </ul>
      </section>
      <section anchor="reconstruction-rules">
        <name>Reconstruction Rules</name>
        <ol spacing="normal" type="1"><li>
            <t>Terms <bcp14>MUST</bcp14> be processed in order.</t>
          </li>
          <li>
            <t>For each term, extract chunks at indices <tt>[start, end)</tt> from the specified xorb.</t>
          </li>
          <li>
            <t>Decompress chunks according to their compression headers.</t>
          </li>
          <li>
            <t>Concatenate decompressed chunk data in order.</t>
          </li>
          <li>
            <t>For range queries, apply <tt>offset_into_first_range</tt> to skip initial bytes.</t>
          </li>
          <li>
            <t>Validate that the total reconstructed size matches expectations.</t>
          </li>
        </ol>
      </section>
      <section anchor="range-queries">
        <name>Range Queries</name>
        <t>When downloading a byte range rather than the complete file:</t>
        <ol spacing="normal" type="1"><li>
            <t>The reconstruction API returns only terms overlapping the requested range.</t>
          </li>
          <li>
            <t>The <tt>offset_into_first_range</tt> field indicates bytes to skip in the first term.</t>
          </li>
          <li>
            <t>The client <bcp14>MUST</bcp14> truncate output to match the requested range length.</t>
          </li>
        </ol>
      </section>
    </section>
    <section anchor="shard-format">
      <name>Shard Format</name>
      <t>A shard is a binary metadata structure that describes file reconstructions and xorb contents.
Shards serve two purposes:</t>
      <ol spacing="normal" type="1"><li>
          <t>Upload Registration: Describing newly uploaded files and xorbs to the CAS server</t>
        </li>
        <li>
          <t>Deduplication Response: Providing information about existing chunks for deduplication</t>
        </li>
      </ol>
      <section anchor="overall-structure">
        <name>Overall Structure</name>
        <artwork><![CDATA[
+--------------------------------------------------------+
|                    Header (48 bytes)                   |
+--------------------------------------------------------+
|                    File Info Section                   |
|              (variable, ends with bookend)             |
+--------------------------------------------------------+
|                    CAS Info Section                    |
|              (variable, ends with bookend)             |
+--------------------------------------------------------+
|                   Footer (200 bytes)                   |
|                (omitted for upload API)                |
+--------------------------------------------------------+
]]></artwork>
      </section>
      <section anchor="data-types">
        <name>Data Types</name>
        <t>All multi-byte integers are little-endian.
Field sizes are stated explicitly (e.g., “8-bit unsigned”, “32-bit unsigned”, “64-bit unsigned”).
Hash denotes a 32-byte (256-bit) value.</t>
      </section>
      <section anchor="shard-header">
        <name>Header</name>
        <t>The header is 48 bytes at offset 0:</t>
        <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       32    Tag (magic identifier)
32      8     Version (64-bit unsigned, MUST be 2)
40      8     Footer Size (64-bit unsigned, 0 if footer omitted)
]]></artwork>
        <t>The header version (<tt>2</tt>) and footer version (<tt>1</tt>) are independent version numbers that may evolve separately.</t>
        <section anchor="magic-tag">
          <name>Magic Tag</name>
          <t>The 32-byte magic tag identifies the shard format and the application deployment:</t>
          <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       14    Application Identifier (ASCII, null-padded)
14      1     Null byte (0x00)
15      17    Magic sequence (fixed)
]]></artwork>
          <t>The magic sequence (bytes 15-31) <bcp14>MUST</bcp14> be exactly:</t>
          <artwork><![CDATA[
SHARD_MAGIC_SEQUENCE = {
  0x55, 0x69, 0x67, 0x45, 0x6a, 0x7b, 0x81, 0x57,
  0x83, 0xa5, 0xbd, 0xd9, 0x5c, 0xcd, 0xd1, 0x4a, 0xa9
}
]]></artwork>
          <t>The application identifier (bytes 0-13) is deployment-specific and identifies the XET application context.
For Hugging Face deployments, the identifier <bcp14>MUST</bcp14> be <tt>"HFRepoMetaData"</tt> (ASCII):</t>
          <artwork><![CDATA[
HF_APPLICATION_ID = {
  0x48, 0x46, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x65,
  0x74, 0x61, 0x44, 0x61, 0x74, 0x61
}
]]></artwork>
          <t>Other deployments <bcp14>MAY</bcp14> define their own application identifiers.
If the identifier is shorter than 14 bytes, it <bcp14>MUST</bcp14> be null-padded on the right.</t>
          <t>Implementations <bcp14>MUST</bcp14> verify that bytes 15-31 match the expected magic sequence before processing.
Implementations <bcp14>MAY</bcp14> additionally verify the application identifier to ensure compatibility with the expected deployment.</t>
        </section>
      </section>
      <section anchor="file-info-section">
        <name>File Info Section</name>
        <t>The file info section contains zero or more file blocks, each describing a file reconstruction.
The section ends with a bookend entry.</t>
        <section anchor="file-block-structure">
          <name>File Block Structure</name>
          <t>Each file block contains:</t>
          <ol spacing="normal" type="1"><li>
              <t><tt>FileDataSequenceHeader</tt> (48 bytes)</t>
            </li>
            <li>
              <t><tt>FileDataSequenceEntry</tt> entries (48 bytes each, count from header)</t>
            </li>
            <li>
              <t><tt>FileVerificationEntry</tt> entries (48 bytes each, if flag set)</t>
            </li>
            <li>
              <t><tt>FileMetadataExt</tt> (48 bytes, if flag set)</t>
            </li>
          </ol>
        </section>
        <section anchor="filedatasequenceheader">
          <name><tt>FileDataSequenceHeader</tt></name>
          <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       32    File Hash
32      4     File Flags (32-bit unsigned)
36      4     Number of Entries (32-bit unsigned)
40      8     Reserved (zeros)
]]></artwork>
          <t>File Flags:</t>
          <table>
            <thead>
              <tr>
                <th align="left">Bit</th>
                <th align="left">Name</th>
                <th align="left">Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">31</td>
                <td align="left">
                  <tt>WITH_VERIFICATION</tt></td>
                <td align="left">
                  <tt>FileVerificationEntry</tt> present for each entry</td>
              </tr>
              <tr>
                <td align="left">30</td>
                <td align="left">
                  <tt>WITH_METADATA_EXT</tt></td>
                <td align="left">
                  <tt>FileMetadataExt</tt> present at end</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="filedatasequenceentry">
          <name><tt>FileDataSequenceEntry</tt></name>
          <t>Each entry describes a term in the file reconstruction:</t>
          <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       32    CAS Hash (xorb hash)
32      4     CAS Flags (32-bit unsigned, reserved, MUST be set to 0)
36      4     Unpacked Segment Bytes (32-bit unsigned)
40      4     Chunk Index Start (32-bit unsigned)
44      4     Chunk Index End (32-bit unsigned, exclusive)
]]></artwork>
          <t>The chunk range is specified as <tt>[chunk_index_start, chunk_index_end)</tt> (end-exclusive).</t>
        </section>
        <section anchor="fileverificationentry">
          <name><tt>FileVerificationEntry</tt></name>
          <t>Present only when <tt>WITH_VERIFICATION</tt> flag is set:</t>
          <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       32    Range Hash (verification hash)
32      16    Reserved (zeros)
]]></artwork>
          <t>The range hash is computed as described in <xref target="verification-hashes"/>.</t>
        </section>
        <section anchor="filemetadataext">
          <name><tt>FileMetadataExt</tt></name>
          <t>Present only when <tt>WITH_METADATA_EXT</tt> flag is set:</t>
          <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       32    SHA-256 Hash of file contents
32      16    Reserved (zeros)
]]></artwork>
        </section>
        <section anchor="bookend-entry">
          <name>Bookend Entry</name>
          <t>The file info section ends with a 48-byte bookend:</t>
          <ul spacing="normal">
            <li>
              <t>Bytes 0-31: All <tt>0xFF</tt></t>
            </li>
            <li>
              <t>Bytes 32-47: All <tt>0x00</tt></t>
            </li>
          </ul>
        </section>
      </section>
      <section anchor="cas-info-section">
        <name>CAS Info Section</name>
        <t>The CAS info section contains zero or more CAS blocks, each describing a xorb and its chunks.
The section ends with a bookend entry.</t>
        <section anchor="cas-block-structure">
          <name>CAS Block Structure</name>
          <t>Each CAS block contains:</t>
          <ol spacing="normal" type="1"><li>
              <t><tt>CASChunkSequenceHeader</tt> (48 bytes)</t>
            </li>
            <li>
              <t><tt>CASChunkSequenceEntry</tt> entries (48 bytes each, count from header)</t>
            </li>
          </ol>
        </section>
        <section anchor="caschunksequenceheader">
          <name><tt>CASChunkSequenceHeader</tt></name>
          <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       32    CAS Hash (xorb hash)
32      4     CAS Flags (32-bit unsigned, reserved, MUST be set to 0)
36      4     Number of Entries (32-bit unsigned)
40      4     Num Bytes in CAS (32-bit unsigned, total uncompressed)
44      4     Num Bytes on Disk (32-bit unsigned, serialized xorb size)
]]></artwork>
        </section>
        <section anchor="caschunksequenceentry">
          <name><tt>CASChunkSequenceEntry</tt></name>
          <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       32    Chunk Hash
32      4     Chunk Byte Range Start (32-bit unsigned)
36      4     Unpacked Segment Bytes (32-bit unsigned)
40      4     Flags (32-bit unsigned)
44      4     Reserved (32-bit unsigned, zeros)
]]></artwork>
          <section anchor="chunk-byte-range-start-calculation">
            <name>Chunk Byte Range Start Calculation</name>
            <t>The <tt>chunk_byte_range_start</tt> field is the cumulative byte offset of this chunk within the uncompressed xorb data.
It is calculated as the sum of <tt>unpacked_segment_bytes</tt> for all preceding chunks in the xorb:</t>
            <artwork><![CDATA[
function calculate_byte_range_starts(chunks):
    position = 0
    for each chunk in chunks:
        chunk.byte_range_start = position
        position += chunk.unpacked_segment_bytes
]]></artwork>
            <t>Example for a xorb with three chunks:</t>
            <artwork><![CDATA[
Chunk 0: unpacked_segment_bytes = 1000
         byte_range_start = 0

Chunk 1: unpacked_segment_bytes = 2000
         byte_range_start = 1000

Chunk 2: unpacked_segment_bytes = 500
         byte_range_start = 3000
]]></artwork>
            <t>This field enables efficient seeking within a xorb without decompressing all preceding chunks.</t>
          </section>
          <section anchor="chunk-flags">
            <name>Chunk Flags</name>
            <table>
              <thead>
                <tr>
                  <th align="left">Bit</th>
                  <th align="left">Name</th>
                  <th align="left">Description</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td align="left">31</td>
                  <td align="left">
                    <tt>GLOBAL_DEDUP_ELIGIBLE</tt></td>
                  <td align="left">Chunk is eligible for global deduplication queries (see <xref target="global-deduplication"/>)</td>
                </tr>
                <tr>
                  <td align="left">0-30</td>
                  <td align="left">Reserved</td>
                  <td align="left">
                    <bcp14>MUST</bcp14> be zero</td>
                </tr>
              </tbody>
            </table>
          </section>
        </section>
        <section anchor="bookend-entry-1">
          <name>Bookend Entry</name>
          <t>The CAS info section ends with a 48-byte bookend (same format as file info bookend).</t>
        </section>
      </section>
      <section anchor="shard-footer">
        <name>Footer</name>
        <t>The footer is 200 bytes at the end of the shard.
It is <bcp14>REQUIRED</bcp14> for stored shards but <bcp14>MUST</bcp14> be omitted when uploading shards via the upload API.</t>
        <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       8     Version (64-bit unsigned, MUST be 1)
8       8     File Info Offset (64-bit unsigned)
16      8     CAS Info Offset (64-bit unsigned)
24      8     File Lookup Offset (64-bit unsigned)
32      8     File Lookup Num Entries (64-bit unsigned)
40      8     CAS Lookup Offset (64-bit unsigned)
48      8     CAS Lookup Num Entries (64-bit unsigned)
56      8     Chunk Lookup Offset (64-bit unsigned)
64      8     Chunk Lookup Num Entries (64-bit unsigned)
72      32    Chunk Hash Key
104     8     Shard Creation Timestamp (64-bit unsigned, Unix epoch seconds)
112     8     Shard Key Expiry (64-bit unsigned, Unix epoch seconds)
120     48    Reserved (zeros)
168     8     Stored Bytes on Disk (64-bit unsigned)
176     8     Materialized Bytes (64-bit unsigned)
184     8     Stored Bytes (64-bit unsigned)
192     8     Footer Offset (64-bit unsigned)
]]></artwork>
        <t>Total size: 200 bytes</t>
        <section anchor="lookup-tables">
          <name>Lookup Tables</name>
          <t>Between the CAS info section and the footer, stored shards include lookup tables for efficient searching:</t>
          <section anchor="file-lookup-table">
            <name>File Lookup Table</name>
            <t>Located at <tt>file_lookup_offset</tt>, contains <tt>file_lookup_num_entries</tt> entries.
Each entry is 12 bytes:</t>
            <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       8     Truncated File Hash (64-bit unsigned, first 8 bytes of hash)
8       4     File Info Entry Index (32-bit unsigned)
]]></artwork>
            <t>Entries are sorted by truncated hash for binary search.</t>
          </section>
          <section anchor="cas-lookup-table">
            <name>CAS Lookup Table</name>
            <t>Located at <tt>cas_lookup_offset</tt>, contains <tt>cas_lookup_num_entries</tt> entries.
Each entry is 12 bytes:</t>
            <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       8     Truncated CAS Hash (64-bit unsigned, first 8 bytes of hash)
8       4     CAS Info Entry Index (32-bit unsigned)
]]></artwork>
            <t>Entries are sorted by truncated hash for binary search.</t>
          </section>
          <section anchor="chunk-lookup-table">
            <name>Chunk Lookup Table</name>
            <t>Located at <tt>chunk_lookup_offset</tt>, contains <tt>chunk_lookup_num_entries</tt> entries.
Each entry is 16 bytes:</t>
            <artwork><![CDATA[
Offset  Size  Field
------  ----  -----
0       8     Truncated Chunk Hash (64-bit unsigned, first 8 bytes of hash)
8       4     CAS Entry Index (32-bit unsigned)
12      4     Chunk Index within CAS (32-bit unsigned)
]]></artwork>
            <t>Entries are sorted by truncated hash for binary search.
When keyed hash protection is enabled, the truncated hash is computed from the keyed chunk hash, not the original.</t>
          </section>
        </section>
        <section anchor="chunk-hash-key-usage">
          <name>Chunk Hash Key Usage</name>
          <t>In global deduplication responses, chunk hashes in the CAS info section are protected with a keyed hash.
Clients <bcp14>MUST</bcp14>:</t>
          <ol spacing="normal" type="1"><li>
              <t>Compute <tt>keyed_hash(footer.chunk_hash_key, their_chunk_hash)</tt> for each local chunk</t>
            </li>
            <li>
              <t>Search for matches in the shard’s CAS info section using the keyed hashes</t>
            </li>
            <li>
              <t>Use matched xorb references for deduplication</t>
            </li>
          </ol>
          <t>If <tt>chunk_hash_key</tt> is all zeros, chunk hashes are stored without keyed hash protection.</t>
        </section>
      </section>
    </section>
    <section anchor="deduplication">
      <name>Deduplication</name>
      <t>XET supports chunk-level deduplication at multiple levels to minimize storage and transfer overhead.</t>
      <section anchor="local-session-deduplication">
        <name>Local Session Deduplication</name>
        <t>Within a single upload session, implementations <bcp14>SHOULD</bcp14> track chunk hashes to avoid processing identical chunks multiple times.</t>
      </section>
      <section anchor="cached-metadata-deduplication">
        <name>Cached Metadata Deduplication</name>
        <t>Implementations <bcp14>MAY</bcp14> cache shard metadata locally to enable deduplication against recently uploaded content without network queries.</t>
      </section>
      <section anchor="global-deduplication">
        <name>Global Deduplication</name>
        <t>The global deduplication API enables discovering existing chunks across the entire storage system.</t>
        <section anchor="eligibility-criteria">
          <name>Eligibility Criteria</name>
          <t>Not all chunks are eligible for global deduplication queries.
A chunk is eligible if:</t>
          <ol spacing="normal" type="1"><li>
              <t>It is the first chunk of a file, OR</t>
            </li>
            <li>
              <t>The last 8 bytes of its hash, interpreted as a little-endian 64-bit unsigned integer, satisfy: <tt>value % 1024 == 0</tt></t>
            </li>
          </ol>
        </section>
        <section anchor="query-process">
          <name>Query Process</name>
          <ol spacing="normal" type="1"><li>
              <t>For eligible chunks, query the global deduplication API (see <xref target="global-dedup-api"/>).</t>
            </li>
            <li>
              <t>On a match, the API returns a shard containing CAS info for xorbs containing the chunk.</t>
            </li>
            <li>
              <t>Chunk hashes in the response are protected with a keyed hash; match by computing keyed hashes of local chunk hashes.</t>
            </li>
            <li>
              <t>Record matched xorb references for use in file reconstruction terms.</t>
            </li>
          </ol>
        </section>
        <section anchor="keyed-hash-security">
          <name>Keyed Hash Security</name>
          <t>The keyed hash protection ensures that clients can only identify chunks they already possess:</t>
          <ol spacing="normal" type="1"><li>
              <t>The server never reveals raw chunk hashes to clients.</t>
            </li>
            <li>
              <t>Clients must compute <tt>keyed_hash(key, local_hash)</tt> to find matches.</t>
            </li>
            <li>
              <t>A match confirms the client has the data, enabling reference to the existing xorb.</t>
            </li>
          </ol>
        </section>
      </section>
      <section anchor="fragmentation-prevention">
        <name>Fragmentation Prevention</name>
        <t>Aggressive deduplication can fragment files across many xorbs, harming read performance.
Implementations <bcp14>SHOULD</bcp14>:</t>
        <ul spacing="normal">
          <li>
            <t>Prefer longer contiguous chunk ranges over maximum deduplication</t>
          </li>
          <li>
            <t>Target minimum run lengths (e.g., 8 chunks or 1 MiB) before accepting deduplicated references</t>
          </li>
        </ul>
      </section>
    </section>
    <section anchor="cas-api">
      <name>CAS API</name>
      <t>The CAS (Content Addressable Storage) API provides HTTP endpoints for upload and download operations.</t>
      <section anchor="authentication">
        <name>Authentication</name>
        <t>All API requests require authentication via Bearer token in the <tt>Authorization</tt> header:</t>
        <artwork><![CDATA[
Authorization: Bearer <access_token>
]]></artwork>
        <t>Tokens have associated scopes:</t>
        <ul spacing="normal">
          <li>
            <t><tt>read</tt>: Required for reconstruction and global deduplication queries</t>
          </li>
          <li>
            <t><tt>write</tt>: Required for xorb and shard uploads (includes <tt>read</tt> permissions)</t>
          </li>
        </ul>
        <t>Token acquisition is provider-specific and outside the scope of this specification.</t>
      </section>
      <section anchor="common-headers">
        <name>Common Headers</name>
        <t>Request headers:</t>
        <ul spacing="normal">
          <li>
            <t><tt>Authorization</tt>: Bearer token (required)</t>
          </li>
          <li>
            <t><tt>Content-Type</tt>: <tt>application/octet-stream</tt> for binary uploads</t>
          </li>
          <li>
            <t><tt>Range</tt>: Byte range for partial requests (optional)</t>
          </li>
        </ul>
        <t>Response headers:</t>
        <ul spacing="normal">
          <li>
            <t><tt>Content-Type</tt>: <tt>application/json</tt> or <tt>application/octet-stream</tt></t>
          </li>
        </ul>
      </section>
      <section anchor="reconstruction-api">
        <name>Get File Reconstruction</name>
        <t>Retrieves reconstruction information for downloading a file.</t>
        <artwork><![CDATA[
GET /v1/reconstructions/{file_id}
]]></artwork>
        <t>Path Parameters:</t>
        <ul spacing="normal">
          <li>
            <t><tt>file_id</tt>: File hash as hex string (see <xref target="hash-string-format"/>)</t>
          </li>
        </ul>
        <t>Optional Headers:</t>
        <ul spacing="normal">
          <li>
            <t><tt>Range: bytes={start}-{end}</tt>: Request specific byte range (end inclusive)</t>
          </li>
        </ul>
        <t>Response (<tt>200 OK</tt>):</t>
        <sourcecode type="json"><![CDATA[
{
  "offset_into_first_range": 0,
  "terms": [
    {
      "hash": "<xorb_hash_hex>",
      "unpacked_length": 263873,
      "range": {
        "start": 0,
        "end": 4
      }
    }
  ],
  "fetch_info": {
    "<xorb_hash_hex>": [
      {
        "range": {
          "start": 0,
          "end": 4
        },
        "url": "https://...",
        "url_range": {
          "start": 0,
          "end": 131071
        }
      }
    ]
  }
}
]]></sourcecode>
        <t>Response Fields:</t>
        <ul spacing="normal">
          <li>
            <t><tt>offset_into_first_range</tt>: Bytes to skip in first term (for range queries)</t>
          </li>
          <li>
            <t><tt>terms</tt>: Ordered list of reconstruction terms</t>
          </li>
          <li>
            <t><tt>fetch_info</tt>: Map from xorb hash to fetch information</t>
          </li>
        </ul>
        <t>Fetch Info Fields:</t>
        <ul spacing="normal">
          <li>
            <t><tt>range</tt>: Chunk index range this entry covers</t>
          </li>
          <li>
            <t><tt>url</tt>: Pre-signed URL for downloading xorb data</t>
          </li>
          <li>
            <t><tt>url_range</tt>: Byte range within the xorb (end inclusive), directly usable as HTTP <tt>Range</tt> header values.
The start offset is always aligned to a chunk header boundary, so clients can parse chunk headers sequentially from the start of the fetched data.</t>
          </li>
        </ul>
        <t>Error Responses:</t>
        <ul spacing="normal">
          <li>
            <t><tt>400 Bad Request</tt>: Invalid <tt>file_id</tt> format</t>
          </li>
          <li>
            <t><tt>401 Unauthorized</tt>: Invalid or expired token</t>
          </li>
          <li>
            <t><tt>404 Not Found</tt>: File does not exist</t>
          </li>
          <li>
            <t><tt>416 Range Not Satisfiable</tt>: Invalid byte range</t>
          </li>
        </ul>
      </section>
      <section anchor="global-dedup-api">
        <name>Query Chunk Deduplication</name>
        <t>Checks if a chunk exists in the system for deduplication.</t>
        <artwork><![CDATA[
GET /v1/chunks/default-merkledb/{chunk_hash}
]]></artwork>
        <t>Path Parameters:</t>
        <ul spacing="normal">
          <li>
            <t><tt>chunk_hash</tt>: Chunk hash as hex string (see <xref target="hash-string-format"/>)</t>
          </li>
        </ul>
        <t>Response (<tt>200 OK</tt>): Shard format binary (see <xref target="shard-format"/>)</t>
        <t>Response (<tt>404 Not Found</tt>): Chunk not tracked by global deduplication</t>
      </section>
      <section anchor="upload-xorb">
        <name>Upload Xorb</name>
        <t>Uploads a serialized xorb to storage.</t>
        <artwork><![CDATA[
POST /v1/xorbs/default/{xorb_hash}
]]></artwork>
        <t>Path Parameters:</t>
        <ul spacing="normal">
          <li>
            <t><tt>xorb_hash</tt>: Xorb hash as hex string (see <xref target="hash-string-format"/>)</t>
          </li>
        </ul>
        <t>Request Body: Serialized xorb (binary, see <xref target="xorb-format"/>)</t>
        <t>Response (<tt>200 OK</tt>):</t>
        <sourcecode type="json"><![CDATA[
{
  "was_inserted": true
}
]]></sourcecode>
        <t>The <tt>was_inserted</tt> field is <tt>false</tt> if the xorb already existed; this is not an error.</t>
        <t>Error Responses:</t>
        <ul spacing="normal">
          <li>
            <t><tt>400 Bad Request</tt>: Hash mismatch or invalid xorb format</t>
          </li>
          <li>
            <t><tt>401 Unauthorized</tt>: Invalid or expired token</t>
          </li>
          <li>
            <t><tt>403 Forbidden</tt>: Insufficient token scope</t>
          </li>
        </ul>
      </section>
      <section anchor="upload-shard">
        <name>Upload Shard</name>
        <t>Uploads a shard to register files in the system.</t>
        <artwork><![CDATA[
POST /v1/shards
]]></artwork>
        <t>Request Body: Serialized shard without footer (binary, see <xref target="shard-format"/>)</t>
        <t>Response (<tt>200 OK</tt>):</t>
        <sourcecode type="json"><![CDATA[
{
  "result": 0
}
]]></sourcecode>
        <t>Result values:</t>
        <ul spacing="normal">
          <li>
            <t><tt>0</tt>: Shard already exists</t>
          </li>
          <li>
            <t><tt>1</tt>: Shard was registered</t>
          </li>
        </ul>
        <t>Error Responses:</t>
        <ul spacing="normal">
          <li>
            <t><tt>400 Bad Request</tt>: Invalid shard format or referenced xorb missing</t>
          </li>
          <li>
            <t><tt>401 Unauthorized</tt>: Invalid or expired token</t>
          </li>
          <li>
            <t><tt>403 Forbidden</tt>: Insufficient token scope</t>
          </li>
        </ul>
      </section>
    </section>
    <section anchor="upload-protocol">
      <name>Upload Protocol</name>
      <t>This section describes the complete procedure for uploading files.</t>
      <section anchor="step-1-chunking">
        <name>Step 1: Chunking</name>
        <t>Split each file into chunks using the algorithm in <xref target="content-defined-chunking"/>.</t>
        <t>For each chunk:</t>
        <ol spacing="normal" type="1"><li>
            <t>Compute the chunk hash (see <xref target="chunk-hashes"/>)</t>
          </li>
          <li>
            <t>Record the chunk data, hash, and size</t>
          </li>
        </ol>
      </section>
      <section anchor="step-2-deduplication">
        <name>Step 2: Deduplication</name>
        <t>For each chunk, attempt deduplication in order:</t>
        <ol spacing="normal" type="1"><li>
            <t>Local Session: Check if chunk hash was seen earlier in this session</t>
          </li>
          <li>
            <t>Cached Metadata: Check local shard cache for chunk hash</t>
          </li>
          <li>
            <t>Global API: For eligible chunks, query the global deduplication API</t>
          </li>
        </ol>
        <t>Record deduplication results:</t>
        <ul spacing="normal">
          <li>
            <t>New chunks: Will be included in xorbs</t>
          </li>
          <li>
            <t>Deduplicated chunks: Record existing xorb hash and chunk index</t>
          </li>
        </ul>
      </section>
      <section anchor="step-3-xorb-formation">
        <name>Step 3: Xorb Formation</name>
        <t>Group new (non-deduplicated) chunks into xorbs:</t>
        <ol spacing="normal" type="1"><li>
            <t>Collect chunks maintaining their order within files</t>
          </li>
          <li>
            <t>Form xorbs targeting ~64 MiB total size</t>
          </li>
          <li>
            <t>Compute compression for each chunk</t>
          </li>
          <li>
            <t>Compute xorb hash for each xorb (see <xref target="xorb-hashes"/>)</t>
          </li>
        </ol>
      </section>
      <section anchor="step-4-xorb-serialization-and-upload">
        <name>Step 4: Xorb Serialization and Upload</name>
        <t>For each new xorb:</t>
        <ol spacing="normal" type="1"><li>
            <t>Serialize using the format in <xref target="xorb-format"/></t>
          </li>
          <li>
            <t>Upload via <tt>POST</tt> to <tt>/v1/xorbs/default/{xorb_hash}</tt></t>
          </li>
          <li>
            <t>Verify successful response</t>
          </li>
        </ol>
        <t>All xorbs <bcp14>MUST</bcp14> be uploaded before proceeding to shard upload.</t>
      </section>
      <section anchor="step-5-shard-formation">
        <name>Step 5: Shard Formation</name>
        <t>Build the shard structure:</t>
        <ol spacing="normal" type="1"><li>
            <t>For each file, construct file reconstruction terms</t>
          </li>
          <li>
            <t>Compute verification hashes for each term (see <xref target="verification-hashes"/>)</t>
          </li>
          <li>
            <t>Compute file hash (see <xref target="file-hashes"/>)</t>
          </li>
          <li>
            <t>Compute SHA-256 of raw file contents</t>
          </li>
          <li>
            <t>Build CAS info blocks for new xorbs</t>
          </li>
        </ol>
      </section>
      <section anchor="step-6-shard-upload">
        <name>Step 6: Shard Upload</name>
        <ol spacing="normal" type="1"><li>
            <t>Serialize the shard without footer</t>
          </li>
          <li>
            <t>Upload via <tt>POST</tt> to <tt>/v1/shards</tt></t>
          </li>
          <li>
            <t>Verify successful response</t>
          </li>
        </ol>
      </section>
      <section anchor="ordering-and-concurrency">
        <name>Ordering and Concurrency</name>
        <t>The following ordering constraints apply:</t>
        <ul spacing="normal">
          <li>
            <t>All xorbs referenced by a shard <bcp14>MUST</bcp14> be uploaded before the shard</t>
          </li>
          <li>
            <t>Chunk computation for a file must complete before xorb formation</t>
          </li>
          <li>
            <t>Xorb hash computation must complete before shard formation</t>
          </li>
        </ul>
        <t>Within these constraints, operations <bcp14>MAY</bcp14> be parallelized:</t>
        <ul spacing="normal">
          <li>
            <t>Multiple files can be chunked concurrently</t>
          </li>
          <li>
            <t>Multiple xorbs can be uploaded concurrently</t>
          </li>
          <li>
            <t>Deduplication queries can run in parallel</t>
          </li>
        </ul>
      </section>
    </section>
    <section anchor="download-protocol">
      <name>Download Protocol</name>
      <t>This section describes the complete procedure for downloading files.</t>
      <section anchor="step-1-query-reconstruction">
        <name>Step 1: Query Reconstruction</name>
        <t>Request reconstruction information:</t>
        <artwork><![CDATA[
GET /v1/reconstructions/{file_id}
Authorization: Bearer <token>
]]></artwork>
        <t>For range queries, include the <tt>Range</tt> header:</t>
        <artwork><![CDATA[
Range: bytes=0-1048575
]]></artwork>
      </section>
      <section anchor="step-2-parse-response">
        <name>Step 2: Parse Response</name>
        <t>Extract from the response:</t>
        <ul spacing="normal">
          <li>
            <t><tt>offset_into_first_range</tt>: Bytes to skip in first term</t>
          </li>
          <li>
            <t><tt>terms</tt>: Ordered list of terms to process</t>
          </li>
          <li>
            <t><tt>fetch_info</tt>: URLs and ranges for downloading data</t>
          </li>
        </ul>
      </section>
      <section anchor="step-3-download-xorb-data">
        <name>Step 3: Download Xorb Data</name>
        <t>For each term:</t>
        <ol spacing="normal" type="1"><li>
            <t>Look up <tt>fetch_info</tt> by xorb hash</t>
          </li>
          <li>
            <t>Find <tt>fetch_info</tt> entry covering the term’s chunk range</t>
          </li>
          <li>
            <t>Make HTTP <tt>GET</tt> request to the URL with <tt>Range</tt> header</t>
          </li>
          <li>
            <t>Download the xorb byte range</t>
          </li>
        </ol>
        <t>Multiple terms may share <tt>fetch_info</tt> entries; implementations <bcp14>SHOULD</bcp14> avoid redundant downloads.</t>
      </section>
      <section anchor="step-4-extract-chunks">
        <name>Step 4: Extract Chunks</name>
        <t>For each downloaded xorb range:</t>
        <ol spacing="normal" type="1"><li>
            <t>Parse chunk headers sequentially</t>
          </li>
          <li>
            <t>Decompress chunk data according to compression type</t>
          </li>
          <li>
            <t>Extract chunks for the term’s index range</t>
          </li>
        </ol>
      </section>
      <section anchor="step-5-assemble-file">
        <name>Step 5: Assemble File</name>
        <ol spacing="normal" type="1"><li>
            <t>For the first term, skip <tt>offset_into_first_range</tt> bytes</t>
          </li>
          <li>
            <t>Concatenate extracted chunks in term order</t>
          </li>
          <li>
            <t>For range queries, truncate to requested length</t>
          </li>
          <li>
            <t>Write to output file or buffer</t>
          </li>
        </ol>
      </section>
      <section anchor="caching-recommendations">
        <name>Caching Recommendations</name>
        <t>See <xref target="caching-considerations"/> for comprehensive caching guidance.
Key recommendations:</t>
        <ul spacing="normal">
          <li>
            <t>Cache decompressed chunks by hash for reuse across files and sessions</t>
          </li>
          <li>
            <t>Avoid caching reconstruction API responses (pre-signed URLs expire quickly)</t>
          </li>
          <li>
            <t>Cache shard metadata for local deduplication during uploads</t>
          </li>
        </ul>
      </section>
      <section anchor="error-handling">
        <name>Error Handling</name>
        <t>Implementations <bcp14>SHOULD</bcp14> implement:</t>
        <ul spacing="normal">
          <li>
            <t>Retry logic with exponential backoff for transient failures</t>
          </li>
          <li>
            <t>Validation of decompressed chunk sizes against headers</t>
          </li>
          <li>
            <t>Hash verification of reconstructed files when possible</t>
          </li>
        </ul>
      </section>
    </section>
    <section anchor="caching-considerations">
      <name>Caching Considerations</name>
      <t>XET’s content-addressable design enables effective caching at multiple levels.
This section provides guidance for implementers on caching strategies and considerations.</t>
      <section anchor="content-immutability">
        <name>Content Immutability</name>
        <t>Objects in XET are identified by cryptographic hashes of their content.
This content-addressable design provides a fundamental property: content at a given hash never changes.
A xorb with hash H will always contain the same bytes, and a chunk with hash C will always decompress to the same data.</t>
        <t>This immutability enables aggressive caching:</t>
        <ul spacing="normal">
          <li>
            <t>Cached xorb data never becomes stale</t>
          </li>
          <li>
            <t>Cached chunk data can be reused indefinitely</t>
          </li>
          <li>
            <t>Cache invalidation is never required for content objects</t>
          </li>
        </ul>
        <t>The only time-sensitive elements are authentication tokens and pre-signed URLs, which are discussed separately below.</t>
      </section>
      <section anchor="client-side-chunk-caching">
        <name>Client-Side Chunk Caching</name>
        <t>Implementations <bcp14>SHOULD</bcp14> cache decompressed chunk data to avoid redundant decompression and network requests.
The chunk hash provides a natural cache key.</t>
        <section anchor="cache-key-design">
          <name>Cache Key Design</name>
          <t>Chunk caches <bcp14>SHOULD</bcp14> use the chunk hash (32 bytes or its string representation) as the cache key.
Since hashes uniquely identify content, there is no risk of cache collisions or stale data.</t>
        </section>
        <section anchor="cache-granularity">
          <name>Cache Granularity</name>
          <t>Implementations <bcp14>MAY</bcp14> cache at different granularities:</t>
          <ul spacing="normal">
            <li>
              <t>Individual chunks: Fine-grained, maximizes deduplication benefit</t>
            </li>
            <li>
              <t>Chunk ranges: Coarser-grained, reduces metadata overhead</t>
            </li>
            <li>
              <t>Complete xorbs: Simplest, but may cache unused chunks</t>
            </li>
          </ul>
          <t>For most workloads, caching individual chunks by hash provides the best balance of storage efficiency and hit rate.</t>
        </section>
        <section anchor="eviction-strategies">
          <name>Eviction Strategies</name>
          <t>Since all cached content remains valid indefinitely, eviction is based purely on resource constraints:</t>
          <ul spacing="normal">
            <li>
              <t>LRU (Least Recently Used): Effective for workloads with temporal locality</t>
            </li>
            <li>
              <t>LFU (Least Frequently Used): Effective for workloads with stable hot sets</t>
            </li>
            <li>
              <t>Size-aware LRU: Prioritizes keeping smaller chunks that are cheaper to re-fetch</t>
            </li>
          </ul>
          <t>Implementations <bcp14>SHOULD</bcp14> track cache size and implement eviction when storage limits are reached.</t>
        </section>
      </section>
      <section anchor="xorb-data-caching">
        <name>Xorb Data Caching</name>
        <t>Raw xorb data (compressed chunks with headers) <bcp14>MAY</bcp14> be cached by clients or intermediaries.</t>
        <section anchor="client-side-xorb-cache">
          <name>Client-Side Xorb Cache</name>
          <t>Caching raw xorb byte ranges avoids repeated downloads but requires decompression on each use.
This uses local storage to reduce bandwidth consumption.
Implementations <bcp14>SHOULD</bcp14> prefer caching decompressed chunks unless bandwidth is severely constrained.</t>
        </section>
        <section anchor="byte-range-considerations">
          <name>Byte Range Considerations</name>
          <t>When caching partial xorb downloads (byte ranges), implementations <bcp14>SHOULD</bcp14>:</t>
          <ol spacing="normal" type="1"><li>
              <t>Cache at chunk-header-aligned boundaries to enable independent chunk extraction</t>
            </li>
            <li>
              <t>Track which byte ranges are cached for each xorb hash</t>
            </li>
            <li>
              <t>Coalesce adjacent cached ranges when possible</t>
            </li>
          </ol>
        </section>
      </section>
      <section anchor="shard-metadata-caching">
        <name>Shard Metadata Caching</name>
        <t>Shard metadata enables deduplication without network queries.
Implementations <bcp14>SHOULD</bcp14> cache shards from recent uploads for local deduplication.</t>
        <section anchor="cache-lifetime">
          <name>Cache Lifetime</name>
          <t>Unlike content objects, shard metadata has implicit lifetime constraints:</t>
          <ul spacing="normal">
            <li>
              <t>Global deduplication responses include a <tt>chunk_hash_key</tt> that rotates periodically</t>
            </li>
            <li>
              <t>The <tt>shard_key_expiry</tt> field in the footer indicates when the key expires</t>
            </li>
            <li>
              <t>After expiry, keyed hash matches will fail</t>
            </li>
          </ul>
          <t>Implementations <bcp14>SHOULD</bcp14> evict cached deduplication shards when their keys expire.</t>
        </section>
        <section anchor="cache-size">
          <name>Cache Size</name>
          <t>Shard metadata is relatively compact (typically under 1 MiB per upload session).
Implementations <bcp14>MAY</bcp14> cache several hundred recent shards without significant storage impact.</t>
        </section>
      </section>
      <section anchor="pre-signed-url-handling">
        <name>Pre-Signed URL Handling</name>
        <t>The reconstruction API returns pre-signed URLs for downloading xorb data.
These URLs have short expiration times (typically minutes to hours) and <bcp14>MUST NOT</bcp14> be cached beyond their validity period.</t>
        <t>Implementations <bcp14>MUST</bcp14>:</t>
        <ul spacing="normal">
          <li>
            <t>Use URLs promptly after receiving them</t>
          </li>
          <li>
            <t>Re-query the reconstruction API if URLs have expired</t>
          </li>
          <li>
            <t>Never persist URLs to disk for later sessions</t>
          </li>
        </ul>
        <t>Reconstruction responses <bcp14>SHOULD</bcp14> be treated as ephemeral and re-fetched when needed rather than cached.</t>
      </section>
      <section anchor="http-caching-headers">
        <name>HTTP Caching Headers</name>
        <section anchor="server-recommendations">
          <name>Server Recommendations</name>
          <t>CAS servers <bcp14>SHOULD</bcp14> return appropriate HTTP caching headers for xorb downloads:</t>
          <t>For xorb content (immutable):</t>
          <artwork><![CDATA[
Cache-Control: public, immutable, max-age=<url_ttl_seconds>
ETag: "<xorb_hash>"
]]></artwork>
          <ul spacing="normal">
            <li>
              <t><tt>max-age</tt> <bcp14>MUST</bcp14> be set to a value no greater than the remaining validity window of the pre-signed URL used to serve the object (e.g., a URL that expires in 900 seconds <bcp14>MUST NOT</bcp14> be served with <tt>max-age</tt> larger than 900).</t>
            </li>
            <li>
              <t>Servers <bcp14>SHOULD</bcp14> also emit an <tt>Expires</tt> header aligned to the URL expiry time.</t>
            </li>
            <li>
              <t>Shared caches <bcp14>MUST NOT</bcp14> serve the response after either header indicates expiry, even if the content is immutable.</t>
            </li>
          </ul>
          <t>The <tt>immutable</tt> directive still applies within that bounded window, allowing caches to skip revalidation until the signature expires.</t>
          <t>For reconstruction API responses (ephemeral):</t>
          <artwork><![CDATA[
Cache-Control: private, no-store
]]></artwork>
          <t>Reconstruction responses contain pre-signed URLs that expire and <bcp14>MUST NOT</bcp14> be cached by intermediaries.</t>
          <t>For global deduplication responses:</t>
          <artwork><![CDATA[
Cache-Control: private, max-age=3600
Vary: Authorization
]]></artwork>
          <t>Deduplication responses are user-specific and may be cached briefly by the client.</t>
        </section>
        <section anchor="client-recommendations">
          <name>Client Recommendations</name>
          <t>Clients <bcp14>SHOULD</bcp14> respect <tt>Cache-Control</tt> headers from servers.
When downloading xorb data, clients <bcp14>MAY</bcp14> cache responses locally even if no caching headers are present, since content-addressed data is inherently immutable.</t>
        </section>
      </section>
      <section anchor="cdn-integration">
        <name>CDN Integration</name>
        <t>XET deployments typically serve xorb data through CDNs.
The content-addressable design is well-suited for CDN caching:</t>
        <ul spacing="normal">
          <li>
            <t>Hash-based URLs enable cache key stability</t>
          </li>
          <li>
            <t>Immutable content eliminates cache invalidation complexity</t>
          </li>
          <li>
            <t>Range requests enable partial caching of large xorbs</t>
          </li>
        </ul>
        <section anchor="cdn-cache-keys">
          <name>CDN Cache Keys</name>
          <t>Effective cache key design determines whether multiple users can share cached xorb data.
Since xorb content is immutable and identified by hash, the ideal cache key includes only the xorb hash and byte range, maximizing cache reuse.
However, access control requirements constrain this choice.</t>
          <t>Two URL authorization strategies are applicable to XET deployments:</t>
          <t><strong>Edge-Authenticated URLs.</strong>
The URL path contains the xorb hash with no signature parameters.
Authorization is enforced at the CDN edge via signed cookies or tokens validated on every request.
The cache key is derived from the xorb hash and byte range only, excluding any authorization tokens.
This allows all authorized users to share the same cache entries.
This pattern requires CDNs capable of per-request authorization; generic shared caches without edge auth <bcp14>MUST NOT</bcp14> be used.</t>
          <t><strong>Query-Signed URLs.</strong>
The URL includes signature parameters in the query string (similar to pre-signed cloud storage URLs).
Cache keys <bcp14>MUST</bcp14> include all signature-bearing query parameters.
Each unique signature produces a separate cache entry, resulting in lower hit rates.
This approach works with any CDN but sacrifices cache efficiency for simplicity.</t>
          <t>For both strategies:</t>
          <ul spacing="normal">
            <li>
              <t>Cache keys <bcp14>SHOULD</bcp14> include the byte range when <tt>Range</tt> headers are present</t>
            </li>
            <li>
              <t>Cache keys <bcp14>SHOULD NOT</bcp14> include <tt>Authorization</tt> headers, since different users have different tokens but request identical content</t>
            </li>
          </ul>
          <t>For deployments with access-controlled content (e.g., gated models requiring user agreement), see <xref target="access-controlled-content"/> for additional CDN considerations.</t>
        </section>
        <section anchor="range-request-caching">
          <name>Range Request Caching</name>
          <t>CDNs <bcp14>SHOULD</bcp14> cache partial responses (<tt>206 Partial Content</tt>) by byte range.
When a subsequent request covers a cached range, the CDN can serve from cache without contacting the origin.</t>
          <t>Some CDNs support range coalescing, where multiple partial caches are combined to serve larger requests.
This is particularly effective for XET where different users may request different chunk ranges from the same xorb.</t>
        </section>
      </section>
      <section anchor="proxy-and-intermediary-considerations">
        <name>Proxy and Intermediary Considerations</name>
        <t>Corporate proxies and other intermediaries <bcp14>MAY</bcp14> cache XET traffic.</t>
        <t>Pre-signed URLs include authentication in the URL itself, allowing unauthenticated intermediaries to cache responses.
However, reconstruction API requests include Bearer tokens in the <tt>Authorization</tt> header and <bcp14>SHOULD NOT</bcp14> be cached by intermediaries (the <tt>private</tt> directive prevents this).</t>
      </section>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <section anchor="content-integrity">
        <name>Content Integrity</name>
        <t>XET provides content integrity through cryptographic hashing:</t>
        <ul spacing="normal">
          <li>
            <t>Chunk hashes verify individual chunk integrity</t>
          </li>
          <li>
            <t>Xorb hashes verify complete xorb contents</t>
          </li>
          <li>
            <t>File hashes verify complete file reconstruction</t>
          </li>
        </ul>
        <t>Implementations <bcp14>SHOULD</bcp14> verify hashes when possible, particularly for downloaded content.</t>
      </section>
      <section anchor="authentication-and-authorization">
        <name>Authentication and Authorization</name>
        <t>Token-based authentication controls access to storage operations.
Implementations <bcp14>MUST</bcp14>:</t>
        <ul spacing="normal">
          <li>
            <t>Transmit tokens only over TLS-protected connections</t>
          </li>
          <li>
            <t>Avoid logging tokens</t>
          </li>
          <li>
            <t>Implement token refresh before expiration</t>
          </li>
          <li>
            <t>Use minimum required scope (prefer read over write)</t>
          </li>
        </ul>
      </section>
      <section anchor="global-deduplication-privacy">
        <name>Global Deduplication Privacy</name>
        <t>The keyed hash protection in global deduplication prevents enumeration attacks:</t>
        <ul spacing="normal">
          <li>
            <t>Servers never reveal raw chunk hashes</t>
          </li>
          <li>
            <t>Clients can only match chunks they possess</t>
          </li>
          <li>
            <t>The chunk hash key rotates periodically, and shard expiry limits the reuse window</t>
          </li>
        </ul>
      </section>
      <section anchor="access-controlled-content">
        <name>Access-Controlled Content</name>
        <t>XET deployments may support access-controlled or “gated” content, where users must be authorized (e.g., by accepting terms of service or requesting access) before downloading certain files.
This has several implications for XET implementations.</t>
        <section anchor="repository-level-access-control">
          <name>Repository-Level Access Control</name>
          <t>Access control in XET is typically enforced at the repository or file level, not at the xorb or chunk level.
The reconstruction API <bcp14>MUST</bcp14> verify that the requesting user has access to the file before returning pre-signed URLs.
Unauthorized requests <bcp14>MUST</bcp14> return <tt>401 Unauthorized</tt> or <tt>403 Forbidden</tt>.</t>
        </section>
        <section anchor="cdn-considerations-for-gated-content">
          <name>CDN Considerations for Gated Content</name>
          <t>Since the same xorb may be referenced by both public and access-controlled files, CDN caching requires careful design:</t>
          <t><strong>Edge-Authenticated Deployments.</strong>
When using edge authentication (cookies or tokens validated per-request), the CDN enforces access control on every request.
Xorbs referenced only by access-controlled files remain protected even when cached.
This is the recommended approach for deployments with gated content.</t>
          <t><strong>Query-Signed URL Deployments.</strong>
When using query-signed URLs, each authorized user receives unique signatures.
Cache efficiency is reduced, but access control is enforced by signature validity.
Deployments <bcp14>MAY</bcp14> choose to exclude xorbs from access-controlled repositories from CDN caching entirely.</t>
        </section>
        <section anchor="cross-repository-deduplication">
          <name>Cross-Repository Deduplication</name>
          <t>The same chunk may exist in both access-controlled and public repositories.
XET’s content-addressable design allows storage deduplication across access boundaries:</t>
          <ul spacing="normal">
            <li>
              <t>When a user uploads to a public repository, chunks matching access-controlled content may be deduplicated</t>
            </li>
            <li>
              <t>The user does not gain access to the access-controlled repository; they simply avoid re-uploading data they already possess</t>
            </li>
            <li>
              <t>The keyed hash protection in global deduplication (<xref target="global-deduplication"/>) ensures users can only match chunks they possess</t>
            </li>
          </ul>
          <t>This is a storage optimization, not an access control bypass.
Implementations <bcp14>MUST</bcp14> still enforce repository-level access control for all download operations.</t>
        </section>
        <section anchor="privacy-implications">
          <name>Privacy Implications</name>
          <t>Deployments with access-controlled content <bcp14>SHOULD</bcp14> consider:</t>
          <ul spacing="normal">
            <li>
              <t>Global deduplication queries reveal chunk existence (via 200/404 responses), though not which repositories contain the chunk</t>
            </li>
            <li>
              <t>Keyed hash protection in responses ensures clients can only identify chunks they already possess; key rotation limits temporal correlation</t>
            </li>
            <li>
              <t>For highly sensitive content, deployments <bcp14>MAY</bcp14> exclude chunks from the global deduplication index entirely</t>
            </li>
          </ul>
        </section>
      </section>
      <section anchor="denial-of-service-considerations">
        <name>Denial of Service Considerations</name>
        <t>Large file uploads could exhaust server resources.
Servers <bcp14>SHOULD</bcp14> implement:</t>
        <ul spacing="normal">
          <li>
            <t>Rate limiting on API endpoints</t>
          </li>
          <li>
            <t>Maximum shard size limits (64 MiB)</t>
          </li>
          <li>
            <t>Maximum xorb size limits (64 MiB)</t>
          </li>
        </ul>
      </section>
    </section>
    <section numbered="false" anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>This document does not require any IANA actions.</t>
    </section>
  </middle>
  <back>
    <references anchor="sec-combined-references">
      <name>References</name>
      <references anchor="sec-normative-references">
        <name>Normative References</name>
        <reference anchor="BLAKE3" target="https://github.com/BLAKE3-team/BLAKE3-specs/blob/master/blake3.pdf">
          <front>
            <title>BLAKE3: One function, fast everywhere</title>
            <author initials="J." surname="Aumasson">
              <organization/>
            </author>
            <author initials="S." surname="Neves">
              <organization/>
            </author>
            <author initials="J." surname="O'Connor">
              <organization/>
            </author>
            <author initials="Z." surname="Wilcox-O'Hearn">
              <organization/>
            </author>
            <date year="2020" month="January" day="09"/>
          </front>
        </reference>
        <reference anchor="LZ4" target="https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md">
          <front>
            <title>LZ4 Frame Format Description</title>
            <author initials="Y." surname="Collet">
              <organization/>
            </author>
            <date year="2015"/>
          </front>
        </reference>
        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
      </references>
      <references anchor="sec-informative-references">
        <name>Informative References</name>
        <reference anchor="GEARHASH" target="https://github.com/srijs/rust-gearhash">
          <front>
            <title>rust-gearhash: Fast, SIMD-accelerated GEAR hashing</title>
            <author initials="S." surname="Rijsdijk">
              <organization/>
            </author>
            <date year="2020"/>
          </front>
        </reference>
        <reference anchor="FASTCDC" target="https://www.usenix.org/conference/atc16/technical-sessions/presentation/xia">
          <front>
            <title>FastCDC: A Fast and Efficient Content-Defined Chunking Approach for Data Deduplication</title>
            <author initials="D." surname="Feng">
              <organization/>
            </author>
            <author initials="Y." surname="Hu">
              <organization/>
            </author>
            <author initials="Y." surname="Hua">
              <organization/>
            </author>
            <author initials="H." surname="Jiang">
              <organization/>
            </author>
            <author initials="Q." surname="Liu">
              <organization/>
            </author>
            <author initials="W." surname="Xia">
              <organization/>
            </author>
            <author initials="Y." surname="Zhang">
              <organization/>
            </author>
            <author initials="Y." surname="Zhou">
              <organization/>
            </author>
            <date year="2016"/>
          </front>
          <seriesInfo name="USENIX ATC 2016" value=""/>
        </reference>
        <reference anchor="MERKLE">
          <front>
            <title>A Digital Signature Based on a Conventional Encryption Function</title>
            <author initials="R. C." surname="Merkle">
              <organization/>
            </author>
            <date year="1987"/>
          </front>
          <seriesInfo name="CRYPTO 1987, LNCS 293, pp. 369-378" value=""/>
        </reference>
      </references>
    </references>
    <?line 1805?>

<section anchor="gearhash-table">
      <name>Gearhash Lookup Table</name>
      <t>The <tt>XET-GEARHASH-BLAKE3</tt> content-defined chunking algorithm requires a lookup table of 256 64-bit constants.
Implementations of this suite <bcp14>MUST</bcp14> use the exact values below for determinism.</t>
      <artwork><![CDATA[
TABLE = [
    0xb088d3a9e840f559, 0x5652c7f739ed20d6, 0x45b28969898972ab, 0x6b0a89d5b68ec777,
    0x368f573e8b7a31b7, 0x1dc636dce936d94b, 0x207a4c4e5554d5b6, 0xa474b34628239acb,
    0x3b06a83e1ca3b912, 0x90e78d6c2f02baf7, 0xe1c92df7150d9a8a, 0x8e95053a1086d3ad,
    0x5a2ef4f1b83a0722, 0xa50fac949f807fae, 0x0e7303eb80d8d681, 0x99b07edc1570ad0f,
    0x689d2fb555fd3076, 0x00005082119ea468, 0xc4b08306a88fcc28, 0x3eb0678af6374afd,
    0xf19f87ab86ad7436, 0xf2129fbfbe6bc736, 0x481149575c98a4ed, 0x0000010695477bc5,
    0x1fba37801a9ceacc, 0x3bf06fd663a49b6d, 0x99687e9782e3874b, 0x79a10673aa50d8e3,
    0xe4accf9e6211f420, 0x2520e71f87579071, 0x2bd5d3fd781a8a9b, 0x00de4dcddd11c873,
    0xeaa9311c5a87392f, 0xdb748eb617bc40ff, 0xaf579a8df620bf6f, 0x86a6e5da1b09c2b1,
    0xcc2fc30ac322a12e, 0x355e2afec1f74267, 0x2d99c8f4c021a47b, 0xbade4b4a9404cfc3,
    0xf7b518721d707d69, 0x3286b6587bf32c20, 0x0000b68886af270c, 0xa115d6e4db8a9079,
    0x484f7e9c97b2e199, 0xccca7bb75713e301, 0xbf2584a62bb0f160, 0xade7e813625dbcc8,
    0x000070940d87955a, 0x8ae69108139e626f, 0xbd776ad72fde38a2, 0xfb6b001fc2fcc0cf,
    0xc7a474b8e67bc427, 0xbaf6f11610eb5d58, 0x09cb1f5b6de770d1, 0xb0b219e6977d4c47,
    0x00ccbc386ea7ad4a, 0xcc849d0adf973f01, 0x73a3ef7d016af770, 0xc807d2d386bdbdfe,
    0x7f2ac9966c791730, 0xd037a86bc6c504da, 0xf3f17c661eaa609d, 0xaca626b04daae687,
    0x755a99374f4a5b07, 0x90837ee65b2caede, 0x6ee8ad93fd560785, 0x0000d9e11053edd8,
    0x9e063bb2d21cdbd7, 0x07ab77f12a01d2b2, 0xec550255e6641b44, 0x78fb94a8449c14c6,
    0xc7510e1bc6c0f5f5, 0x0000320b36e4cae3, 0x827c33262c8b1a2d, 0x14675f0b48ea4144,
    0x267bd3a6498deceb, 0xf1916ff982f5035e, 0x86221b7ff434fb88, 0x9dbecee7386f49d8,
    0xea58f8cac80f8f4a, 0x008d198692fc64d8, 0x6d38704fbabf9a36, 0xe032cb07d1e7be4c,
    0x228d21f6ad450890, 0x635cb1bfc02589a5, 0x4620a1739ca2ce71, 0xa7e7dfe3aae5fb58,
    0x0c10ca932b3c0deb, 0x2727fee884afed7b, 0xa2df1c6df9e2ab1f, 0x4dcdd1ac0774f523,
    0x000070ffad33e24e, 0xa2ace87bc5977816, 0x9892275ab4286049, 0xc2861181ddf18959,
    0xbb9972a042483e19, 0xef70cd3766513078, 0x00000513abfc9864, 0xc058b61858c94083,
    0x09e850859725e0de, 0x9197fb3bf83e7d94, 0x7e1e626d12b64bce, 0x520c54507f7b57d1,
    0xbee1797174e22416, 0x6fd9ac3222e95587, 0x0023957c9adfbf3e, 0xa01c7d7e234bbe15,
    0xaba2c758b8a38cbb, 0x0d1fa0ceec3e2b30, 0x0bb6a58b7e60b991, 0x4333dd5b9fa26635,
    0xc2fd3b7d4001c1a3, 0xfb41802454731127, 0x65a56185a50d18cb, 0xf67a02bd8784b54f,
    0x696f11dd67e65063, 0x00002022fca814ab, 0x8cd6be912db9d852, 0x695189b6e9ae8a57,
    0xee9453b50ada0c28, 0xd8fc5ea91a78845e, 0xab86bf191a4aa767, 0x0000c6b5c86415e5,
    0x267310178e08a22e, 0xed2d101b078bca25, 0x3b41ed84b226a8fb, 0x13e622120f28dc06,
    0xa315f5ebfb706d26, 0x8816c34e3301bace, 0xe9395b9cbb71fdae, 0x002ce9202e721648,
    0x4283db1d2bb3c91c, 0xd77d461ad2b1a6a5, 0xe2ec17e46eeb866b, 0xb8e0be4039fbc47c,
    0xdea160c4d5299d04, 0x7eec86c8d28c3634, 0x2119ad129f98a399, 0xa6ccf46b61a283ef,
    0x2c52cedef658c617, 0x2db4871169acdd83, 0x0000f0d6f39ecbe9, 0x3dd5d8c98d2f9489,
    0x8a1872a22b01f584, 0xf282a4c40e7b3cf2, 0x8020ec2ccb1ba196, 0x6693b6e09e59e313,
    0x0000ce19cc7c83eb, 0x20cb5735f6479c3b, 0x762ebf3759d75a5b, 0x207bfe823d693975,
    0xd77dc112339cd9d5, 0x9ba7834284627d03, 0x217dc513e95f51e9, 0xb27b1a29fc5e7816,
    0x00d5cd9831bb662d, 0x71e39b806d75734c, 0x7e572af006fb1a23, 0xa2734f2f6ae91f85,
    0xbf82c6b5022cddf2, 0x5c3beac60761a0de, 0xcdc893bb47416998, 0x6d1085615c187e01,
    0x77f8ae30ac277c5d, 0x917c6b81122a2c91, 0x5b75b699add16967, 0x0000cf6ae79a069b,
    0xf3c40afa60de1104, 0x2063127aa59167c3, 0x621de62269d1894d, 0xd188ac1de62b4726,
    0x107036e2154b673c, 0x0000b85f28553a1d, 0xf2ef4e4c18236f3d, 0xd9d6de6611b9f602,
    0xa1fc7955fb47911c, 0xeb85fd032f298dbd, 0xbe27502fb3befae1, 0xe3034251c4cd661e,
    0x441364d354071836, 0x0082b36c75f2983e, 0xb145910316fa66f0, 0x021c069c9847caf7,
    0x2910dfc75a4b5221, 0x735b353e1c57a8b5, 0xce44312ce98ed96c, 0xbc942e4506bdfa65,
    0xf05086a71257941b, 0xfec3b215d351cead, 0x00ae1055e0144202, 0xf54b40846f42e454,
    0x00007fd9c8bcbcc8, 0xbfbd9ef317de9bfe, 0xa804302ff2854e12, 0x39ce4957a5e5d8d4,
    0xffb9e2a45637ba84, 0x55b9ad1d9ea0818b, 0x00008acbf319178a, 0x48e2bfc8d0fbfb38,
    0x8be39841e848b5e8, 0x0e2712160696a08b, 0xd51096e84b44242a, 0x1101ba176792e13a,
    0xc22e770f4531689d, 0x1689eff272bbc56c, 0x00a92a197f5650ec, 0xbc765990bda1784e,
    0xc61441e392fcb8ae, 0x07e13a2ced31e4a0, 0x92cbe984234e9d4d, 0x8f4ff572bb7d8ac5,
    0x0b9670c00b963bd0, 0x62955a581a03eb01, 0x645f83e5ea000254, 0x41fce516cd88f299,
    0xbbda9748da7a98cf, 0x0000aab2fe4845fa, 0x19761b069bf56555, 0x8b8f5e8343b6ad56,
    0x3e5d1cfd144821d9, 0xec5c1e2ca2b0cd8f, 0xfaf7e0fea7fbb57f, 0x000000d3ba12961b,
    0xda3f90178401b18e, 0x70ff906de33a5feb, 0x0527d5a7c06970e7, 0x22d8e773607c13e9,
    0xc9ab70df643c3bac, 0xeda4c6dc8abe12e3, 0xecef1f410033e78a, 0x0024c2b274ac72cb,
    0x06740d954fa900b4, 0x1d7a299b323d6304, 0xb3c37cb298cbead5, 0xc986e3c76178739b,
    0x9fabea364b46f58a, 0x6da214c5af85cc56, 0x17a43ed8b7a38f84, 0x6eccec511d9adbeb,
    0xf9cab30913335afb, 0x4a5e60c5f415eed2, 0x00006967503672b4, 0x9da51d121454bb87,
    0x84321e13b9bbc816, 0xfb3d6fb6ab2fdd8d, 0x60305eed8e160a8d, 0xcbbf4b14e9946ce8,
    0x00004f63381b10c3, 0x07d5b7816fcc4e10, 0xe5a536726a6a8155, 0x57afb23447a07fdd,
    0x18f346f7abc9d394, 0x636dc655d61ad33d, 0xcc8bab4939f7f3f6, 0x63c7a906c1dd187b
]
]]></artwork>
      <t>This table is from the <tt>rust-gearhash</tt> crate <xref target="GEARHASH"/>.</t>
    </section>
    <section anchor="test-vectors">
      <name>Test Vectors</name>
      <t>The following test vectors are for the <tt>XET-GEARHASH-BLAKE3</tt> algorithm suite.</t>
      <section anchor="chunk-hash-test-vector">
        <name>Chunk Hash Test Vector</name>
        <artwork><![CDATA[
Input (ASCII): Hello World!
Input (hex): 48656c6c6f20576f726c6421

Hash (raw hex, bytes 0-31):
  a29cfb08e608d4d8726dd8659a90b9134b3240d5d8e42d5fcb28e2a6e763a3e8

Hash (XET string representation):
  d8d408e608fb9ca213b9909a65d86d725f2de4d8d540324be8a363e7a6e228cb
]]></artwork>
      </section>
      <section anchor="hash-string-conversion-test-vector">
        <name>Hash String Conversion Test Vector</name>
        <t>The XET hash string format interprets the 32-byte hash as four little-endian 64-bit unsigned values and prints each as 16 hexadecimal digits.</t>
        <artwork><![CDATA[
Hash bytes [0..31]:
  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
  10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f

Expected XET string:
  07060504030201000f0e0d0c0b0a090817161514131211101f1e1d1c1b1a1918
]]></artwork>
        <t>See the <tt>hash_to_string</tt> function in <xref target="hash-string-format"/> for the conversion algorithm.</t>
      </section>
      <section anchor="internal-node-hash-test-vector">
        <name>Internal Node Hash Test Vector</name>
        <artwork><![CDATA[
Child 1:
  hash (XET string): c28f58387a60d4aa200c311cda7c7f77f686614864f5869eadebf765d0a14a69
  size: 100

Child 2:
  hash (XET string): 6e4e3263e073ce2c0e78cc770c361e2778db3b054b98ab65e277fc084fa70f22
  size: 200

Buffer being hashed (ASCII, with literal \n newlines):
  c28f58387a60d4aa200c311cda7c7f77f686614864f5869eadebf765d0a14a69 : 100\n
  6e4e3263e073ce2c0e78cc770c361e2778db3b054b98ab65e277fc084fa70f22 : 200\n

Result (XET string):
  be64c7003ccd3cf4357364750e04c9592b3c36705dee76a71590c011766b6c14
]]></artwork>
      </section>
      <section anchor="verification-range-hash-test-vector">
        <name>Verification Range Hash Test Vector</name>
        <t>Input: Two chunk hashes from test vector B.3, concatenated as raw bytes (not XET string format).</t>
        <artwork><![CDATA[
Chunk hash 1 (raw hex):
  aad4607a38588fc2777f7cda1c310c209e86f564486186f6694aa1d065f7ebad

Chunk hash 2 (raw hex):
  2cce73e063324e6e271e360c77cc780e65ab984b053bdb78220fa74f08fc77e2

Concatenated input (64 bytes, raw hex):
  aad4607a38588fc2777f7cda1c310c209e86f564486186f6694aa1d065f7ebad
  2cce73e063324e6e271e360c77cc780e65ab984b053bdb78220fa74f08fc77e2

Verification hash (XET string):
  eb06a8ad81d588ac05d1d9a079232d9c1e7d0b07232fa58091caa7bf333a2768
]]></artwork>
      </section>
      <section anchor="reference-files">
        <name>Reference Files</name>
        <t>Complete reference files including sample chunks, xorbs, and shards are available at:
https://huggingface.co/datasets/xet-team/xet-spec-reference-files</t>
      </section>
    </section>
    <section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>The XET protocol was invented by Hailey Johnson and Yucheng Low at Hugging Face.
This specification is based on the reference implementation and documentation developed by the Hugging Face team.</t>
    </section>
  </back>
  <!-- ##markdown-source:
H4sIAAAAAAAAA9W9+XrbyLUv+j+fAsf97RMpEWkAnJXunCPLcts7nmLZmTq+
IoaChJgitQnStrbb+1nus9wnu781VKEAkrI73ck52/milkighlVrnqrb7XbW
5XpujoN7fz57fRycLhdrs1h3T/J8ZaoqSecmOF8vV8mlCV6ulutltpwHxXIV
nBVFmZV4NHiYrJPg9SpZVIVZ3eskaboy72W8e518mS2Sawyfr5Ji3c3Noqy6
H826G8adLFmby+Xq9jgoF8WyU96sjoP1alOt4zCc4vtqk16XVVUuF69vbww9
lZsbgx+Ldeeduf2wXOXHwROsd7XAgA9pgk6yWV8tV8edIOgGMvEjrOxd8JAm
xqdBsFxdJovyP5M1xqXX3Zi891WZbrBdftJcJ+X8OChy87/DsOhhkk6OJWNr
cRgP73WwyX6nWieL/CKZLxf44tZUneo6Wa0v/mOzXJtKPrkpj4MfALmjoFqu
1itTVPjt9pp+edvpLJarayzmvaFFP3h68vuz/jFPb89FPwteLExQbBYZLfwo
KJJqHZj3ZnX74cqszD1+pd49/esCYljCv/eCk811UlXLRfOL817wHCNUW4+/
+BVAsVAouC/+2gv+VM6z5cfui189NslKRhOIACBhN4y64VSWnqwuzfo4uFqv
b6rj+/cvy/XVJu1ly+v7spvu2iTu9+rGZNX9dL5M72OZOE78nrwz/d5NXmC4
p38dNAGCD+hUr03wiGGHw62yVXlDgLkDDn/p4YTnc5yiv+5o+KUVz/9zwP/n
Bebm/X3gNH1wwWu4KHgNveu80yE09g7z+7OTV49Pzh83V08I3r0E/K6S6gro
iR0fBedPnj3sJllm5maFheX8bkBPlIvLO/aEI3xV/r3Ky7+/a53Gl3ZVrfDe
/cZi8Mqjk/PXpw9PmyumJdKHwQmvNgDCe+RvOcZDU5QLrPz0arN4h1UHJzc3
q2WSXTG7YCbx0OSbm3mZJV84qYe94JFZXG4d3+PNjo+S5mePe8G/l0n75T/0
gqdl6+0/9YI/l8nWiH+92nqbP11umngz4j8rsypNRQdv9/Dm/Oz5kz8HJ69P
66fa5/Dhw4fepgJL+tgDO7qfLRdgnWaRmfvJOotG99cmu1oATvNuZZgBVvdv
wI4BZgbd/Y+87mdnr37/9Kx5VifBwxKnnMyD8/Jykaw3KxM8SCqcy3IRJHRY
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
y9QQWD7fajYuz60TxLUh911lgashEufilWklD9VWjfMHOV4mTs5e5/HyAykX
R4GUTvGigPjWQBVccWq8bTG3LDNmsR+WzOwTn5wbHvGVa7pNmwP7bKEhEOTX
vz7LL03XKx9TROn9+teMfTTDTSJGrvR8aG6VpRkoqea+Ny5Xt9cMX0vnAiBp
Js0luEUAjt5gBZzOofwyWy7fleJDVEesvXGeO5wTxG4tnimJ1KdCBhx4n9/8
YN+58MlpV1spD1rctqAp86s3IJEbxshpVadOKtZpcpCp/eeyJtcFg0e4ocS9
1aJ2QBCV48kbPiCQBrTMro0FN1by2+DSLOi6TJnGCXNrGjAM6Y2GYCH9pUeH
zHkKnm3QOF6HybvO0Jpwojm71Gq5Q1NC807QZfPlJneWCU0DbebUno1qHs7M
nM/rCbupSXhkmcZHIW4fIh5hf4GrpXhIE+d89yB+e6QpguLcDOjC9JXzS7rz
JJWXRifL3jY8AwoQTpKbqEoyDqc53uf5ObkNmTW5b1VQp0v29FkC9CKjvH0b
LvSSMzxklOa9jXh/Qx7tHIvO2Y63u2SzsnKsdmgLxrKVU3+olGbdY4SAXiMF
YZayS1+OCcyYeXWVec09Z66qyZdMutfLnBpJCO5zqLUi7RVKOjO6Q5uFvTVc
V4fTGHV9v4AIu+2Yn70wzObhOJcPk1vDU1MXOzqNcBaHI8pT4M81ejg7JGFQ
n5YqIXRrbarpCw5sUplEYTfPMXXkuB3LJxb6cgUfL8OSMTPZbG2TR6Q5C/Z0
vrw2wiy0e4ciTSbeMDxvL8Z10tAXvNaxtrxOy4Vv0qjx4QeLpLSA36auoStS
mBqebRIiMlcbpUgvtFCov2vUaNclSsQj63rzl6vlR4kePKlV4Nstx+bpckVO
eUnT+mjDvkvWA5q6s6cB0oJBlES8Pe6Y3VDMHT9qhgCV7TF/5LsbPXtks0ga
8rI183rZVj09Sb/TGlGVyS7Fr/l1DHg3efP+PWZwhzURHPAwahn4JtmNlPdX
rGAcys1x2kJhOyug0m+20wL8YDsrvxxlI+i7KJLTuuz3Tt/djre7gLLfREHv
E2kHrOoB/XTI+vnMj5nVSavdunx3x7M7UnH3uh31XR2p4YQ+ahKT71yrWeWu
Kn4+26axJrXpqs+3MFYZZmWVybriqtEtYK8f7TXlo5CTQfGO9VruqfD66Xm3
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

-->

</rfc>
