Nix Store Paths — Pill 18
Distinguish source / output / fixed-output paths by what each hash binds.
<!-- hal:authoritative:yaml -->
Name three path kinds. Say what each hash binds. Predict the out path from inputs before you build.
§I — Frame
Asr session 13. Thirteenth live fire of the Nix language track. Week 4 fire 3. The page is Nix Pills Nix Store Paths, Pill 18. Luca Bruno wrote the series. License CC BY-SA 4.0. Ground from the on-disk EPUB at . Live URL is canonical if the EPUB drifts: https://nixos.org/guides/nix-pills/18-nix-store-paths.html. Dolstra 2006 may sit as a second cite when you care about hashing history; the Pill is primary.
Session 12 wired packageOverrides through the fixed point. This session asks a different question: how does Nix decide the /nix/store/…-name string before the builder even runs?
Done-criteria from the syllabus: you can distinguish source paths, output paths, and fixed-output paths.
Not Pill 15 (NIX_PATH; dropped). Not flakes. Not install. Not Python wrapping. Launch a terminal when you have Nix. If Nix is not on the machine today, read the nix-repl and nix-hash numbers from the Pill and from this lesson.
§II — One recipe, three labels
Nix computes store paths in three steps, with small label differences per kind:
- Hash the relevant content (file, NAR, or
.drvwith outs blanked). - Build a description string that includes type tag, hash algo, hash, store root, and name.
- Take sha256 of that string, truncate to 160 bits, encode base32. That digest is the store path prefix.
Musashi declarative: the path is a digest of a typed description, not a free-form filename.
§III — Source paths (named technique: Source Seal)
Yagyu names the cut once. Source Seal: copy a path into the store by hashing its contents, then seal the name into the description string.
Pill walk with echo mycontent > myfile:
$ nix-hash --type sha256 myfile
2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3
$ echo -n "source:sha256:2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3:/nix/store:myfile" > myfile.str
$ nix-hash --type sha256 --truncate --base32 --flat myfile.str
xv2iccirbrvklck36f1g7vldn5v58vck
So ./myfile lands at /nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile. Same path from nix-store --add myfile. Flat hash for a regular file; recursive / NAR for directories. Relative paths in a derivation become inputSrcs after this seal.
If-then-thus: if two trees NAR-hash the same and share the name in the description, then they collide on the same store path, thus content identity is the gate and the filename alone is not.
§IV — Output paths (named technique: Output Predict)
Output Predict: the out path depends on the derivation inputs, so Nix can name /nix/store/…-foo before realize.
Simplest derivation shape from the Pill:
nix-repl> derivation { system = "x86_64-linux"; builder = ./myfile; name = "foo"; }
«derivation /nix/store/…-foo.drv»
Inspect the .drv. You already see outputs.out.path even with no build. Computation:
- Take the
.drvwith each out path replaced by the empty string (Nix’s intermediate state while naming outs). - sha256 that
.drvtext. - Description:
output:out:sha256:<drvhash>:/nix/store:foo(oroutput:<name>:…for multiple outputs). - Truncate and base32 as before.
Input .drv references inside that text are themselves replaced by the same algorithm’s hashes. The final out path is a pure function of the input graph. Builders do not pick the name; they must produce content at the predicted path.
§V — Fixed-output paths (named technique: Fixed Bind)
Fixed Bind: declare outputHash, outputHashAlgo, and outputHashMode. The out path then depends on the declared integrity hash, not on which fetchers or helpers you used as inputs.
nix-repl> derivation {
name = "bar";
system = "x86_64-linux";
builder = "none";
outputHashMode = "flat";
outputHashAlgo = "sha256";
outputHash = "f3f3c4763037e059b4d834eaf68595bbc02ba19f6d2a500dce06d124e2cd99bb";
}
The .drv records that this is fixed-output. Nix builds an intermediate string such as fixed:out:sha256:<contenthash>:, hashes that, then runs the ordinary output:out:… naming step with that intermediate hash. Result: two fetchers that promise the same tarball hash share one store path. The builder must produce content that matches outputHash or the build fails.
nixpkgs uses Fixed Bind for source tarballs. That is why transparent mirrors work: path identity follows the integrity hash.
§VI — Distinguishing table (done-criteria)
| Kind | What is hashed first | Description tag | Out path depends on |
|---|---|---|---|
| Source | File / NAR contents | source:… | Content + name |
| Output | .drv with outs blanked (and input-drv digests) | output:out:… (or output:<name>:) | Derivation inputs |
| Fixed-output | Declared content hash (via fixed:out:… intermediate) | then output:out:… | Declared outputHash (+ name), not fetcher inputs |
Say the table aloud once. Source Seal copies known bytes. Output Predict names build products from the input graph. Fixed Bind pins downloads and other known-byte products to an integrity hash.
§VIb — Thesis as second cite only
Dolstra 2006 may appear when you want hashing history or NAR background. Keep it secondary. The Pill’s three-step recipe (content hash, description string, truncated path digest) is enough to meet today’s done-criteria. Do not rewrite this session as a thesis seminar.
§VII — Common mistakes
- Treating the 32-char prefix as a random id instead of a truncated hash of a typed string.
- Expecting a builder to choose a different out path than the
.drvalready named. - Confusing source seals (
./file→inputSrcs) with fixed-output tarball fetches. - Believing fixed-output paths still shift when you swap mirrors that share
outputHash. - Jumping to flakes path schemas before you can state Source Seal, Output Predict, and Fixed Bind in Pill language.
- Re-opening Pill 15 search paths as if they named store paths (Pill 15 stays dropped).
§VIII — Boundaries
- Not Pill 15 NIX_PATH.
- Not flakes.
- Not install (Pills 1-3).
- Not Python
withPackages/ wrap (later spine). - Do not re-teach
packageOverridesbeyond naming yesterday’s fixed point as prior composition work. - Thesis is optional second cite for hashing history only; Pill 18 remains primary.
§IX — Close
Session 12 recomposed the package set. Session 13 names what lands under /nix/store: Source Seal for inputs you copy, Output Predict for ordinary builds, Fixed Bind for integrity-pinned outputs. Next unmarked: session 14, Pills 19, stdenv.
Related:
Write three one-line definitions (source / output / fixed-output). Point at one real path on disk for each kind if Nix is present, then leave the keyboard.