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, 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, and compute just its
identifier with snapdir id.
Line format
Each non-comment, non-empty line is one entry with exactly five single-space-separated fields:
TYPE PERMS CHECKSUM SIZE PATH
Example — two empty files in a directory:
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.txtsurvives 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--absolutethe full path is kept verbatim. -
Symlinks have no type of their own. A followed symlink is recorded as the type of its target (
ForD);--no-followexcludes symlinks entirely. -
Ordering is by
PATHusing byte-wise (C-locale)sort -k5semantics — 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:
- Take the
CHECKSUMfield of each direct child entry. - Sort them lexicographically (byte-wise).
- Deduplicate (
sort -u). - Concatenate with no separator.
- 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. 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 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 theCHECKSUMfield of the rootD ./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 — the hashing model manifests build on.
-
Stores and cache — how a manifest and its objects are stored and shared.
-
Integrity — how manifests and objects are verified.
-
Commands:
snapdir manifest,snapdir id, and the snapshotting guide.