# Creating a manifest without snapdir A snapdir manifest is plain text: five space-separated columns, one line per file or directory, hashed with [BLAKE3](https://github.com/BLAKE3-team/BLAKE3). There is no binary container, no database, and no proprietary format — which means you can recreate (or verify) every checksum in a manifest, including the snapshot ID itself, using nothing but `b3sum` and standard shell tools. This guide does exactly that, by hand. It's worth doing once: it shows what `snapdir manifest` computes under the hood, and it demonstrates a property that matters in the long run — anyone you hand a snapshot to (a colleague, an external reviewer, an auditor, your future self after snapdir's binaries are long gone) can independently verify its contents with off-the-shelf tools, in any language, without trusting or even installing snapdir. You'll need `snapdir` ([install](../install.md)) and the BLAKE3 CLI, `b3sum` (`cargo install b3sum`, or `brew install b3sum` on macOS). Everything else is POSIX shell. ## Build an example directory First, create a small directory tree to snapshot. Setting `umask 077` first makes the permission columns below reproducible (directories `700`, files `600`): ```sh umask 077 mkdir -p example/a echo a1 > example/a/a1 echo a2 > example/a/a2 echo base > example/base ``` Ask snapdir for its manifest: ```sh snapdir manifest ./example ``` ```text D 700 4257cc46336b9d0ae70a3104ae0382ac6a75da0ee49ffe69b423997e872276a7 11 ./ D 700 40bdff878af8e7ffbc40f1d4b5a72c892a0773df2d47cd164c2dc2e684299dfa 6 ./a/ F 600 92719755f8d6c804d44192bb5835654d27003fc8fdbb36a633b9063c7f9396a4 3 ./a/a1 F 600 ff3e86a123552d66c31eb3308916d76bf9d918b1f635aa39d00d3a3428bda536 3 ./a/a2 F 600 b9af5f26c46534d25add40a12c3f0b1ae926e39a2e669162664295040943f54a 5 ./base ``` Each line is `PATH_TYPE PERMISSIONS CHECKSUM SIZE PATH`: the entry type (`F` file, `D` directory), its octal permissions, its BLAKE3 checksum, its size in bytes (for a directory, the summed size of its contents), and its path (directories end with `/`). Lines are sorted by path, and lines starting with `#` are comments, ignored everywhere. The full column-by-column specification is in [Manifests](../concepts/manifests.md) — the rest of this guide recreates the checksum column from scratch. ## File checksums are just BLAKE3 A file's checksum is the plain BLAKE3 hash of its content, so `b3sum` produces it directly: ```sh b3sum --no-names ./example/a/a1 ``` ```text 92719755f8d6c804d44192bb5835654d27003fc8fdbb36a633b9063c7f9396a4 ``` The same works for `./example/a/a2` (`ff3e86a1…`) and `./example/base` (`b9af5f26…`) — compare each against the manifest above. Nothing snapdir-specific so far: any BLAKE3 implementation gives the same answers. ## Directory checksums: a merkle hash of the children A directory's checksum is computed from its **direct children's checksums**: sort them, drop duplicates, concatenate them into one long hex string with no separators, and hash that string. In shell, that recipe is `sort -u | tr -d '\n' | b3sum`. Recreate the checksum of `./a/`, whose children are the two files we just hashed: ```sh b3sum ./example/a/* --no-names | sort -u | tr -d '\n' | b3sum --no-names ``` ```text 40bdff878af8e7ffbc40f1d4b5a72c892a0773df2d47cd164c2dc2e684299dfa ``` That matches the `D … ./a/` line in the manifest. The root directory works the same way — its children are the directory `./a/` (using the checksum we just computed, not re-walking its contents) and the file `./base`: ```sh { b3sum ./example/a/* --no-names | sort -u | tr -d '\n' | b3sum --no-names b3sum --no-names ./example/base } | sort -u | tr -d '\n' | b3sum --no-names ``` ```text 4257cc46336b9d0ae70a3104ae0382ac6a75da0ee49ffe69b423997e872276a7 ``` — the checksum on the manifest's `D … ./` line. Because each directory hashes its children's *checksums*, a change to any file ripples up through every parent directory to the root: a merkle tree, built with three lines of shell. Note the `-u` in `sort -u`: duplicate checksums among a directory's children are dropped before hashing. Two identical files contribute one checksum, so a directory holding `foo.txt` and `bar.txt` — both empty, both hashing to `af1349b9…` — has the checksum of that *single* deduplicated value: ```sh echo -n af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262 | b3sum --no-names ``` ```text dba5865c0d91b17958e4d2cac98c338f85cbbda07b71a020ab16c391b5e7af4b ``` This is the same identity-comes-from-content rule that gives snapdir its [deduplication](../concepts/content-addressing.md) — a directory's checksum depends on *what* it contains, not how many copies. ## The snapshot ID: hash of the manifest itself The snapshot ID is **not** the root directory's checksum — it is the BLAKE3 hash of the entire manifest text, with comment lines stripped. That makes the ID cover everything the manifest records: permissions, sizes, and paths, not just content. The recipe: ```sh snapdir manifest ./example | grep -v '^#' | b3sum --no-names ``` ```text 7ecd37f57f9d4b4128c4fe07c53e28e668c4f1df6bc6692155737d0ebdc81f8d ``` And the proof that we've reconstructed the whole pipeline: ```sh snapdir id ./example ``` ```text 7ecd37f57f9d4b4128c4fe07c53e28e668c4f1df6bc6692155737d0ebdc81f8d ``` ## Why this matters Everything snapdir later [pushes, verifies, and restores](pushing-pulling.md) is anchored to checksums you can recompute with a few lines of portable shell. In practice that means: - **No lock-in.** The manifest is the format. A third-party tool — or a 20-line script — can produce or consume snapdir manifests without linking any snapdir code. - **Independent verification.** Someone receiving a directory and its manifest can confirm, file by file and as a whole, that what they received is exactly what was recorded — using only standard tools they already trust. That's valuable wherever evidence has to hold up to outside scrutiny: handoffs to reviewers or authorities, archival storage, air-gapped environments. - **Interop with existing tooling.** `snapdir manifest --checksum-bin md5sum` (or `sha256sum`) emits the same five columns keyed on those digests for systems that already track them, and setting `SNAPDIR_MANIFEST_CONTEXT` switches to keyed BLAKE3 for domain-separated IDs — see [Integrity](../concepts/integrity.md). ## Where to go next - [Manifests](../concepts/manifests.md) — the full line-format specification. - [Content addressing](../concepts/content-addressing.md) — why checksums double as storage addresses. - [Integrity](../concepts/integrity.md) — how these checksums are re-verified on every transfer. - Reference: [`snapdir manifest`](../reference/snapdir-manifest.md) and [`snapdir id`](../reference/snapdir-id.md).