cli.sgit.ai / Dev packs / Pack indexes, 01: the format

01. The format

1. On the host

bare/packs/
  commits/    <epoch>__<commit>__<bytes>.zip      data packs, as in 0.24.0
  snapshots/  <epoch>__<commit>__<bytes>.zip
  archive/    <epoch>__<commit>__<bytes>.zip
  indexes/
    commits/    <epoch>__<commit>__<bytes>.zip    the index of commits/<epoch>__<commit>__*.zip
    snapshots/  <epoch>__<commit>__<bytes>.zip    the index of snapshots/<epoch>__<commit>__*.zip
    archive/    <epoch>__<commit>__<bytes>.zip    moved with the data pack they index

2. The zip

2.1 The header member

offset 0   8 bytes   magic b'SGIX\x00\x01\x00\x00'          (format 1)
offset 8   4 bytes   L, big-endian: length of the envelope
offset 12  L bytes   Vault__Crypto.encrypt(read_key, zlib.compress(header_json, 6))
           ...       zero bytes up to the member's reserved size R

header_json, canonical (sorted keys, compact separators, UTF-8, the same form as Vault__Commit.signing_bytes):

{
  "schema": "pack_index_v1",
  "kind": "commit",
  "commit_id": "obj-cas-imm-…",
  "parents": ["obj-cas-imm-…"],
  "generation": 20003,
  "tree_id": "obj-cas-imm-…",
  "timestamp_ms": 1761480000000,
  "data_packs": ["commits/1761480000__obj-cas-imm-…__171234.zip"],
  "members": {
    "changes":            [1, 16384, 412,  "sha256 of the member's plaintext JSON"],
    "folder:raw/c09/s4":  [2, 16828, 233,  "…"],
    "folder:":            [9, 18190, 120,  "…"]
  },
  "author_key_id": "key-rnd-imm-…",
  "signature": "…"
}
FieldMeaning
kindcommit (a delta) or snapshot (the whole head)
commit_id, parents, generation, tree_id, timestamp_msthe commit the pack is named by; tree_id must equal the commit object's
data_packsthe data packs that at fields point into, by index into this list; a commit index lists its own pack and the older packs its folders' unchanged files live in
memberslogical name → [sequence number, offset of the member's bytes in the zip, length, sha256 hex of the plaintext JSON]
author_key_id, signatureECDSA P-256 over the canonical JSON without signature, with the pusher's clone key (PKI__Crypto.sign), as a commit is signed

2.2 Every other member

Vault__Crypto.encrypt(read_key, zlib.compress(member_json, 6)). This is a random IV, so index packs are not byte-deterministic, and they need not be: their names carry their size, and readers accept any verified one. Deterministic encryption would show the host which folders are unchanged between two snapshots.

3. Members

The values in the examples are illustrative. Paths are the vault's paths, /-separated, no leading slash; the root folder is "". Ids are the vault's object ids. Field names follow the flat cache (Vault__Sub_Tree, FLAT_CACHE_VERSION = 2) so a reader can seed it directly.

3.1 flat: snapshot only

The head's flat map, byte for byte the flat cache's format:

{"version": 2, "tree_id": "obj-cas-imm-…",
 "flat": {"raw/c09/s4/pub19/page18/2026-10-21T0100Z/meta.json":
            {"blob_id": "obj-cas-imm-…", "size": 74, "content_hash": "5c40ff698f27",
             "content_type": "application/json", "large": false}},
 "dirs": {"": "obj-cas-imm-…", "raw": "obj-cas-imm-…"}}

Measured: 76,000 files and 58,062 folders, 17.2 MB as JSON, 2.42 MB deflated, 0.58 s to inflate and parse.

3.2 folder:<path>: snapshot (every folder) and commit (the folders on changed paths)

{"schema": "folder_v1", "path": "raw/c09/s4", "tree_id": "obj-cas-imm-…",
 "files": {"meta.json": {"blob_id": "obj-cas-imm-…", "size": 74, "content_hash": "…",
                         "content_type": "application/json", "large": false,
                         "at": [0, 1048576, 160]}},
 "dirs":  {"pub19": "obj-cas-imm-…"},
 "tree_at": [0, 52, 412],
 "totals": {"files": 1520, "bytes": 259292, "folders": 1161}}

Measured on the benchmark vault: median 226 bytes deflated, 11.8 MB for all 58,062.

3.3 changes: commit only

{"schema": "changes_v1", "commit_id": "obj-cas-imm-…", "parents": ["obj-cas-imm-…"],
 "generation": 20003, "timestamp_ms": 1761480000000, "tree_id": "obj-cas-imm-…",
 "changes": [
   {"path": "raw/c09/s4/pub19/page18/x/text.md", "op": "M",
    "old_blob": "obj-cas-imm-…", "new_blob": "obj-cas-imm-…", "size": 276,
    "content_hash": "…", "content_type": "text/markdown", "large": false, "at": [0, 2088, 310]},
   {"path": "raw/c01/old.md", "op": "D", "old_blob": "obj-cas-imm-…"}
 ]}

3.4 commits:<n> and commit-gen: snapshot only

{"obj-cas-imm-…": {"parents": ["…"], "generation": 4097, "timestamp_ms": 1761480000000, "tree_id": "…"}}

3.5 blob-paths:<xx> and file-commits:<xx>: snapshot only

MemberHoldsShard key
blob-paths:<xx>{blob id: [paths that hold it in the head]}xx = the first two hex digits of the blob id's hash part
file-commits:<xx>{path: [commit ids that changed it, newest first]}, from the start of history or the vault's shallow boundaryxx = the first two hex digits of sha256(path); inside the encryption, so the hash leaks nothing

There are 256 of each. Commit indexes do not carry them; a reader applies changes to its own copy (03).

4. Data packs in path order: Phase 2

Vault__Packs.write_pack today writes members in sorted id order. From Phase 2 it writes them in this order:

  1. the commit objects, by id;
  2. tree objects in the depth-first order of their paths (root first, children by name);
  3. blob objects by the path of their first use in that order, each once.

The ids and the member rule (MEMBER_RE: only bare/data/<object id>, stored) do not change. extract_pack sorts by name before writing, and pack verify checks each member's hash whatever the order, so 0.24.0 to 0.25.0 read path-ordered packs as before.

What the order shows the host: which members sit next to each other, so which blobs share a folder and roughly how large each folder is. It does not show names. 04, section 2 sets the rule.

5. Sizes

IndexBenchmark vault (76,000 files)Newsroom vault (79,465 files), estimated
snapshot indexabout 15 MB: flat 2.4 MB, folders 11.8 MB, commits and cross-references about 1 MB each per 20,000 commits15 to 25 MB, against a 1.22 GB snapshot data pack
commit index, five files changed3.1 KB, 27 membersa pass of 6,807 files: about 0.5 MB of changes plus the folders on the paths

6. Versioning

This page as markdown · its source, admin/content/dev/pack-indexes/01-format.md