<?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.39 (Ruby 3.4.9) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-denis-xet-05" category="info" submissionType="independent" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.34.0 -->
  <front>
    <title abbrev="XET">XET: Content-Addressable Storage Protocol for Efficient Data Transfer</title>
    <seriesInfo name="Internet-Draft" value="draft-denis-xet-05"/>
    <author initials="F." surname="Denis" fullname="Frank Denis">
      <organization>Independent Contributor</organization>
      <address>
        <email>fde@00f.net</email>
      </address>
    </author>
    <date year="2026"/>
    <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-BLAKE3-GEARHASH-LZ4</tt> algorithm suite as the default, using <tt>BLAKE3</tt> for cryptographic hashing, <tt>Gearhash</tt> for content-defined chunking, and <tt>LZ4</tt> for compression.</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 in this document use exclusive end: <tt>[start, end)</tt>.
Example: <tt>{"start": 0, "end": 4}</tt> means indices 0, 1, 2, 3.</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>Cryptographic Hash Function: The hash algorithm used for all content addressing (chunk hashes, xorb hashes, file hashes, verification hashes).</t>
          </li>
          <li>
            <t>Content-Defined Chunking Algorithm: The rolling hash function and boundary detection logic used to split files into chunks.</t>
          </li>
          <li>
            <t>Compression Format: The compression algorithm used for chunk data within xorbs.</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-BLAKE3-GEARHASH-LZ4</tt>: Uses <tt>BLAKE3</tt> for all cryptographic hashing, <tt>Gearhash</tt> for content-defined chunking, and <tt>LZ4</tt> for compression. 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-BLAKE3-GEARHASH-LZ4</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-BLAKE3-GEARHASH-LZ4</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-BLAKE3-GEARHASH-LZ4</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-BLAKE3-GEARHASH-LZ4</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-BLAKE3-GEARHASH-LZ4</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 boundary checks until <tt>chunk_size</tt> reaches <tt>MIN_CHUNK_SIZE</tt>, since boundaries are forbidden before that point.</t>
        <t>They <bcp14>MUST</bcp14> still update the rolling hash for every byte; skipping hash updates would change <tt>h</tt> and therefore alter boundary placement, violating determinism.</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-BLAKE3-GEARHASH-LZ4</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-BLAKE3-GEARHASH-LZ4</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 ordered chunk content, not a byte-for-byte serialization.
The hash is computed using a Merkle tree construction where leaf nodes are chunk hashes.
Compression framing and ignored footer fields, such as the uniqueness nonce, are excluded.
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-BLAKE3-GEARHASH-LZ4</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.
For non-empty files, the computation is similar to xorb hashes, but with an additional final keyed hash step for domain separation.
Empty files are a special case and use <tt>ZERO_HASH</tt> directly.</t>
        <t>For the <tt>XET-BLAKE3-GEARHASH-LZ4</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)
    if n == 0:
        return ZERO_HASH
    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_file_hash([], [])</tt> returns <tt>ZERO_HASH</tt> (32 zero bytes).
The final <tt>ZERO_KEY</tt> keyed hash is not applied to empty files.</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-BLAKE3-GEARHASH-LZ4</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, 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 raw payload size
MAX_XORB_CHUNKS = 8192      # Maximum chunks per xorb
]]></artwork>
        <t>Implementations <bcp14>MUST NOT</bcp14> exceed either limit.
<tt>MAX_XORB_SIZE</tt> applies to the sum of uncompressed chunk data bytes in the xorb; the serialized xorb can be larger due to headers, footer metadata, and compression framing.
When collecting chunks:</t>
        <ol spacing="normal" type="1"><li>
            <t>Stop if adding the next chunk’s uncompressed bytes 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>Footer buffer: 16 bytes. The first 4 bytes <bcp14>MAY</bcp14> contain a uniqueness nonce and <bcp14>MUST</bcp14> be ignored by readers. The remaining 12 bytes are reserved and <bcp14>MUST</bcp14> be 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 any of these conditions is true:</t>
          <ol spacing="normal" type="1"><li>
              <t>It is the first chunk of a file, OR</t>
            </li>
            <li>
              <t>Its shard CAS entry has the <tt>GLOBAL_DEDUP_ELIGIBLE</tt> flag set, 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.</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="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 to the CAS server</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 to the CAS server</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 file reconstruction information from the CAS server by providing the file hash.
For partial downloads (range queries), specify the desired byte range.</t>
      </section>
      <section anchor="step-2-parse-response">
        <name>Step 2: Parse Response</name>
        <t>The reconstruction response provides:</t>
        <ul spacing="normal">
          <li>
            <t>Bytes to skip in the first term (for range queries)</t>
          </li>
          <li>
            <t>An ordered list of terms to process</t>
          </li>
          <li>
            <t>URLs and byte ranges for downloading xorb 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>Identify the xorb and byte range needed for the term’s chunk range</t>
          </li>
          <li>
            <t>Download the xorb data from the provided URL</t>
          </li>
          <li>
            <t>Use HTTP range requests when only a portion of the xorb is needed</t>
          </li>
        </ol>
        <t>Multiple terms may reference the same xorb; 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 decompressed chunks in the same order, and a chunk with hash C will always decompress to the same data.
The serialized bytes of a xorb can vary in ignored footer fields such as the uniqueness nonce.</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>
          <t>When used as an ETag, the xorb hash validates the semantic xorb content (the ordered decompressed chunks), not byte-for-byte identity of the serialized response.</t>
          <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.</t>
        <t>However, reconstruction API requests include authentication tokens and <bcp14>SHOULD NOT</bcp14> be cached by intermediaries.</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</t>
          </li>
          <li>
            <t>Maximum xorb size limits (<tt>MAX_XORB_SIZE</tt>, 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 1626?>

<section anchor="recommended-api">
      <name>Recommended HTTP API</name>
      <t>This appendix defines a recommended HTTP API for CAS servers implementing the XET protocol.
This is informative guidance; deployments <bcp14>MAY</bcp14> use different URL structures, authentication mechanisms, or transport protocols entirely.</t>
      <section anchor="authentication">
        <name>Authentication</name>
        <t>API requests requiring authorization use a Bearer token in the <tt>Authorization</tt> header:</t>
        <artwork><![CDATA[
Authorization: Bearer <access_token>
]]></artwork>
        <t>Token format, acquisition and refresh mechanisms are deployment-specific.</t>
      </section>
      <section anchor="common-headers">
        <name>Common Headers</name>
        <t>Request headers:</t>
        <ul spacing="normal">
          <li>
            <t><tt>Authorization</tt>: Bearer token (when authentication is 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 downloads (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="get-file-reconstruction">
        <name>Get File Reconstruction</name>
        <t>Retrieves reconstruction information for downloading a file.</t>
        <artwork><![CDATA[
GET /api/v1/reconstructions/{file_hash}
]]></artwork>
        <t>Path parameters:</t>
        <ul spacing="normal">
          <li>
            <t><tt>file_hash</tt>: File hash as hex string (see <xref target="hash-string-format"/>)</t>
          </li>
        </ul>
        <t>Optional request headers:</t>
        <ul spacing="normal">
          <li>
            <t><tt>Range: bytes={start}-{end}</tt>: Request reconstruction for a specific byte range</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</t>
          </li>
        </ul>
        <t>Chunk index ranges (<tt>range</tt> fields) continue to use the document-wide <tt>[start, end)</tt> convention (exclusive end; see <xref target="notational-conventions"/>), while <tt>url_range</tt> follows HTTP Range semantics and is therefore inclusive.</t>
        <t>Error responses:</t>
        <ul spacing="normal">
          <li>
            <t><tt>400 Bad Request</tt>: Invalid file hash format</t>
          </li>
          <li>
            <t><tt>401 Unauthorized</tt>: Missing or invalid 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="query-chunk-deduplication">
        <name>Query Chunk Deduplication</name>
        <t>Checks if a chunk exists in the global deduplication index.</t>
        <artwork><![CDATA[
GET /api/v1/chunks/{namespace}/{chunk_hash}
]]></artwork>
        <t>Path parameters:</t>
        <ul spacing="normal">
          <li>
            <t><tt>namespace</tt>: Deduplication namespace (e.g., <tt>default-merkledb</tt>)</t>
          </li>
          <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>The returned shard contains CAS info for xorbs that include the queried chunk.
Chunk hashes in the response are protected with a keyed hash (see <xref target="global-deduplication"/>).</t>
        <t>Response (<tt>404 Not Found</tt>): Chunk is 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 /api/v1/xorbs/{namespace}/{xorb_hash}
Content-Type: application/octet-stream
]]></artwork>
        <t>Path parameters:</t>
        <ul spacing="normal">
          <li>
            <t><tt>namespace</tt>: Storage namespace (e.g., <tt>default</tt>)</t>
          </li>
          <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>: Missing or invalid 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 /api/v1/shards
Content-Type: application/octet-stream
]]></artwork>
        <t>Request body: Serialized shard without footer (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>: Missing or invalid token</t>
          </li>
          <li>
            <t><tt>403 Forbidden</tt>: Insufficient token scope</t>
          </li>
        </ul>
      </section>
    </section>
    <section anchor="gearhash-table">
      <name>Gearhash Lookup Table</name>
      <t>The <tt>XET-BLAKE3-GEARHASH-LZ4</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-BLAKE3-GEARHASH-LZ4</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 the Internal Node Hash Test Vector above, 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:
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
B8fcP3aEbhHLC9VvbGEPWxebysQjE4SVFEJOC+Hy+ZbTnNckKeGCzPaiHp8/
fnb5jBdlliMZMO8/uyqXGioD7nCNZj3lhVkPY/c8O52xHdDHSjrRCpkR0mpN
d4t9K3784klnxVZ7ic+v2vvyVZuidGnSBUoZqn9kH1oDdN83fgbHOW0F25sm
ECQsDsqF9o7ocq9qBZS+rBa58bjvMDC3PHF9ceVRhgL7pXFy3oTLUVOiUJnY
/VrGwN7bzbfSUEDvWvqRgCwbeG+naGL/sFiCBDMr+H2g8nlQNKk1dEmSZWIA
S1PJXEjxNVN+73PH7RwYZ7c0BXivFnhDtLKoQZfdyyKJ2KgRe3cw7l1vPdXt
/MEMTId8dE8iU+IlEzVYffcvmTvtTAbzix+efUdHju7cpTuioFQNhZpRIOrm
iMlWKXHjbsG7+aQUGawiCaEM5U5D7VfxfEszwEDo6r04g/QY6MMRr/ROZs9O
JrbOa3vn4k6fYmNkUb2nHYdlF6hNZtZulzsGBsR0bTvPa2kZuVnwXbi1zqCR
tbB3ndpZ7LpcRRKg2x2dKVC7o101WdW/5+0rTbLkpdYaxO9OnZTtpr6DJh1e
d2ItStHTzF0+ctQm2FcbnktpaUfSrMvOv+e7fXI7/d5UqImeoaewWRWHeI7A
eFiyV5FvYaLYIhddtRyhoC+iUGe3KvxNJSrds5G+Sx530G48l61OSHQY1P3o
qa57Qlj1We9sPNXYKFC20b7UoSFhc+cF7Y47bm+1wk4yYJkcjq2sVHi9LyMX
OmlQze7bkqXZwYh30LpHVBq6O1PmNleYCHZFPOGKniOXBfWuoV7QWP/LrvBo
iK7KMjdNEinnEVOtbJRWHJl35smautooOjzdXYROhFbzZ16NWCG6rzr3UeBk
PHrM7rqpA0zkfkK8Z1cOs6DWKR+/OjN5yHy9Nh02NtaaSQsitmeo9zp2j2RP
vRTC7geNMAg8dL7Ml+0YAzDL/sEph7o+lrvcMrZpV7L0MKYiUQTWriU1HWQe
OnZ3fOAxlKlsZ4FwZ3ty0v3GV3cNe5ecFk0MnQyy5WYFpoJuBS799S+opf4q
MS3gdcd5LCK70ynUua6gWOy9DWDrDgRsr7zEOOWRXBpOwVGrR36Sope1ez3C
1l0XhIDo18cSY4sf/qrg/q64OPyTUOdBOG6CeN1/vyIkvWdWEkgUIRclfOes
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

-->

</rfc>
