# Manifests A **manifest** is the heart of a snapdir snapshot: a UTF-8 text document that fully describes a directory tree — every file and directory, its permissions, its content checksum, and its size. Because the description is entirely [content-addressed](content-addressing.md), a manifest is a portable, deduplicating snapshot. Two trees with identical content produce **byte-identical manifests** and therefore the same **snapshot ID**. You can produce a manifest without writing anything to a store with [`snapdir manifest`](../reference/snapdir-manifest.md), and compute just its identifier with [`snapdir id`](../reference/snapdir-id.md). ## Line format Each non-comment, non-empty line is one entry with exactly five single-space-separated fields: ```text TYPE PERMS CHECKSUM SIZE PATH ``` | Field | Meaning | | ---------- | ------------------------------------------------------------------ | | `TYPE` | `F` for a regular file, `D` for a directory. | | `PERMS` | Octal permission string from `stat`, e.g. `700`, `600`. | | `CHECKSUM` | Lowercase hex content checksum of the entry (see below). | | `SIZE` | Content size in bytes. | | `PATH` | The entry's path, taken verbatim. | Example — two empty files in a directory: ```text D 700 dba5865c0d91b17958e4d2cac98c338f85cbbda07b71a020ab16c391b5e7af4b 0 ./ F 600 af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262 0 ./bar.txt F 600 af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262 0 ./foo.txt ``` Some details that matter for round-tripping: - **Paths may contain spaces.** Lines are split on only the *first four* spaces; the fifth field, `PATH`, is taken verbatim, so `./a file with spaces.txt` survives intact. - **Directories always end with `/`** — `./`, `./a/`, `./a/aa/`. In relative mode (the default) every path is prefixed with `./` and the tree root is the `./` entry; with `--absolute` the full path is kept verbatim. - **Symlinks have no type of their own.** A followed symlink is recorded as the type of its target (`F` or `D`); `--no-follow` excludes symlinks entirely. - **Ordering is by `PATH`** using byte-wise (C-locale) `sort -k5` semantics — purely on the path bytes, not on type or checksum. So `./a/` sorts before `./a/a1f`, and a larger checksum can precede a smaller one when the paths demand it. - **Comments (`#…`) and empty lines** are ignored and are excluded from the snapshot ID. ## Directory checksums (the merkle rule) A directory's `CHECKSUM` is **not** a hash of its own metadata. It is a **merkle** derivation from the checksums of its *direct children*: 1. Take the `CHECKSUM` field of each direct child entry. 2. Sort them lexicographically (byte-wise). 3. Deduplicate (`sort -u`). 4. Concatenate with no separator. 5. Re-hash the result with the active checksum function. This makes the directory hash **order-independent** and lets identical subtrees collapse, which is the structural side of snapdir's [deduplication](content-addressing.md#deduplication-falls-out-for-free). Two edge cases follow directly: - An **empty directory** hashes the empty string, giving `af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262`. - A directory of **two empty files** dedupes their identical checksums to one value, giving `dba5865c0d91b17958e4d2cac98c338f85cbbda07b71a020ab16c391b5e7af4b`. ## The snapshot ID The **snapshot ID** is what [`snapdir id`](../reference/snapdir-id.md) reports and the key under which a manifest is stored. It is a **distinct value from the root directory checksum**: > The snapshot ID is the **BLAKE3 hash of the entire `#`-stripped manifest > text**, including the single trailing newline after the last line — *not* the > `CHECKSUM` field of the root `D ./` line. Confusing the two is the classic snapdir foot-gun. For the two-empty-files manifest above, the root `D ./` checksum is `dba5865c…` while the snapshot ID is `c678a299…`. Reproducing the golden IDs requires that trailing newline; hashing the text without it does not match. The snapshot ID is always derived with default BLAKE3, independent of any `--checksum` mode chosen for the per-entry checksums. Because the ID is a hash of the whole document, it changes if *anything* in the tree changes — a byte in a file (which changes that file's checksum, hence its line, hence the manifest), a permission bit, a rename, or an added/removed entry. That is what makes it a faithful, verifiable identity for a directory state. ## Where to go next - [Content addressing](content-addressing.md) — the hashing model manifests build on. - [Stores and cache](stores-and-cache.md) — how a manifest and its objects are stored and shared. - [Integrity](integrity.md) — how manifests and objects are verified. - Commands: [`snapdir manifest`](../reference/snapdir-manifest.md), [`snapdir id`](../reference/snapdir-id.md), and the [snapshotting guide](../guide/snapshotting.md).