references and closures
how nix tracks what depends on what, and what that means for your builds.
how nix tracks what depends on what, and what that means for your builds.
chapter 1 showed how store paths are computed from inputs and how the build sandbox keeps them honest. but a built package does not exist in isolation. at runtime it links against libraries, calls interpreters, reads data files. all of those live at other store paths.
nix needs to know which paths a built package actually uses at runtime. not which ones the derivation declared as build inputs: which ones ended up embedded in the output itself.
after a build completes, nix scans every file in the output. it searches for 32-character strings from nix's base-32 alphabet. any sequence that matches a known store path hash is a reference. that path's hash was found in the output, so the output needs that path at runtime.
the scanner is not parsing ELF headers or interpreting shebangs. it is a string search. it finds the hash, records the dependency.
every path a built package references gets stored in nix's database for that output path.
references compose. if curl references openssl, and openssl references glibc, then anything that needs curl also needs openssl and glibc.
the complete transitive set of a path and everything it references, recursively, is its closure.
curl-8.18.0 alone has 13 direct references and a closure of 22 paths. the whole thing is about 60 MiB. that is what ships when you deploy curl with nix.
references explains how the scanner works and what build-time versus runtime dependencies actually mean.
closures and the garbage collector covers how nix decides what to keep and what to delete.
self-references is the odd case: packages that contain their own store path in their output.