OpenWeights

Read path

From hf download through reconstruction and signed URLs back to bytes.

The request sequence

Step by step

1. Metadata

GET /api/models/{owner}/{repo} returns the file list, sizes, hashes, and commit. Public and unlisted repositories answer without a credential; a private one needs a bearer token identifying its owner.

2. Resolve

GET /{owner}/{repo}/resolve/{revision}/{path} answers 302 with Location pointing at /xet/files/{hash} or /lfs/objects/{oid}, plus x-linked-size and x-linked-etag.

When OPENWEIGHTS_GATEWAY_READS is true and the file is a Xet file, it also sets X-Xet-Hash and X-Xet-Refresh-Route. Those two headers are what make hf_xet take the reconstruction path instead of following the redirect. The redirect stays as the path for clients that do not read them.

hf_hub resolves the head commit first and then requests files under /resolve/<40-hex sha>/<path>, so the CAS accepts both main and a commit SHA.

3. Read token

X-Xet-Refresh-Route points at GET /api/models/{owner}/{repo}/xet-read-token/{revision}, which returns {casUrl, accessToken, exp}. It requires the read scope.

4. Reconstruction

hf_xet asks the CAS how to rebuild the file. There are two shapes.

V1, GET /v1/reconstructions/{file_id}:

{
  "offset_into_first_range": 0,
  "terms": [
    { "hash": "<xorb hash hex>", "unpacked_length": 65536, "range": { "start": 0, "end": 4 } }
  ],
  "fetch_info": {
    "<xorb hash hex>": [
      { "range": { "start": 0, "end": 4 },
        "url": "<signed gateway URL>",
        "url_range": { "start": 0, "end": 32767 } }
    ]
  }
}

V2, GET /v2/reconstructions/{file_id}, differs only in the fetch map. It is keyed xorbs, holds one entry per xorb rather than per merged range, and each entry carries a single multi-range URL:

{
  "offset_into_first_range": 0,
  "terms": [ ... ],
  "xorbs": {
    "<xorb hash hex>": [
      { "url": "<signed multi-range gateway URL>",
        "ranges": [ { "chunks": { "start": 0, "end": 4 },
                      "bytes":  { "start": 0, "end": 32767 } } ] }
    ]
  }
}

Two range conventions coexist and mixing them corrupts downloads. range and chunks are chunk indices, end-exclusive. url_range and bytes are byte offsets, end-inclusive, matching HTTP Range semantics. xet-core walks the fetch map by matching fetch.range.start == term.range.end, which only works while the chunk ranges stay end-exclusive.

Map keys are emitted in sorted order so responses are deterministic.

A batch form exists at GET /v1/reconstructions and GET /reconstructions, returning a map of file id to response, with missing ids omitted.

Concurrent requests for the same file collapse onto one computation through Redis-backed coalescing.

A range starting at or past the end of the file returns 416, which xet-core treats as end-of-data and stops fetching.

5. Byte fetch

hf_xet requests the byte ranges from the gateway. The gateway verifies the signature, checks the requested Range sits inside the granted segments, and serves 206, as multipart/byteranges when more than one range is requested.

On a cache miss it fetches the whole xorb from Sia, hash-verifies it before writing it to the disk cache, and serves the requested range from that file. Concurrent misses for the same xorb collapse onto one Sia download.

See Gateway for the full contract.

6. Reassembly

hf_xet decompresses the chunks in term order and verifies the file against its content hash.

Signed-URL expiry

URLs carry an expiry, GATEWAY_URL_TTL_SECS, default 7200. An expired URL returns 403, and xet-core responds by asking the CAS for a fresh reconstruction. 403 specifically: 401 or 410 would not trigger the refresh path.

Reading without the gateway

OPENWEIGHTS_GATEWAY_READS selects the path. Set to true, the Compose default, the CAS advertises reconstruction and the gateway serves bytes. Set to anything else, resolve omits the Xet headers, clients follow the 302, and GET /xet/files/{hash} has the CAS reconstruct and serve the bytes itself.

That handler joins the reconstruction terms in order, loads each xorb, slices the term's byte window, walks the chunk headers in that slice, and decompresses to the response.

A download can 404

Reconstruction resolves against xorbs that are pinned. Before the pin lands it returns 404. On a fresh indexer the first pin forms on-chain contracts and takes minutes. See Choose an indexer.

On this page