DEV TOOLS Signal 390
The Nix sandbox is a hidden input
Illustration only Photo by Amsterdam City Archives on Unsplash
A post argues that Nix's sandbox-paths setting is an undeclared input to derivations, because it lives outside the .drv and can silently change build outputs without affecting the output hash.
For teams that treat Nix as a reproducibility boundary, this is a practical hermeticity gap: two machines can run the same .drv, produce different files, and still agree on the output path, which weakens trust in cached binaries and in `--check` as a verification step. Auditing a derivation is not sufficient; you also need to know how every Nix binary in your build path was compiled, since the default sandbox list is baked in at build time rather than declared per-derivation.
Written by elseif from the cluster below · every claim links back to a sourceThe three things worth knowing
Nix's sandbox-paths option is configured outside the derivation, so it can change what the builder sees without being recorded in the .drv file.
The default sandbox-paths value is a compile-time property of the Nix binary itself, meaning two `nix --version` outputs that match can still imply different sandbox defaults.
Builders that probe the filesystem can pick up these hidden mounts, so a build that looks byte-reproducible on one machine may not be on another, even with the same .drv.
THE READ
What elseif makes of it.
The technical claim is narrow and concrete. Nix's input-addressed model puts the build recipe inside the .drv and derives the output path from that recipe, with `ca-derivations` available as a separate output-addressed path. The post's point is that the sandbox itself is a configuration channel that sits next to the derivation rather than inside it: the recipe says "run this script in a sandbox," and a separate option decides which host files are visible in that sandbox. Because the option is not part of the .drv, two evaluators can reach the same output path while running different build steps. That is a definitional break with the intensional model, not a corner case in it.
What makes the gap practically dangerous is where the default comes from. The author points at the Nix source itself: on Linux and FreeBSD, when compiled with `SANDBOX_SHELL` defined, the binary seeds `sandbox-paths` with an entry that mounts `/bin/sh` from a specific store path. Two Nix builds with identical version strings can therefore ship with different implicit sandboxes, and the divergence is invisible to any tool that only inspects a derivation. Operators who reason about Nix in terms of pinned inputs and locked channels do not have a corresponding handle on the daemon that runs the build.
The standard reproducibility defenses do not catch this. `nix-build --check` compares the output against a previously built copy, but if both builds see the same hidden input, the comparison still passes. Binary caches behave the same way: the cache key is the output path, and the output path is the same regardless of which sandbox was used to produce it, so a malicious or drifted sandbox on a single builder can poison a cache entry that everyone else will then trust. The only signal in the .drv is the comparison with `__noChroot`, which is a real derivation attribute and is therefore auditable, but ordinary sandbox mounts have no such marker.
The concrete consequence for a build engineer is that the contract "the .drv describes the build" no longer holds by itself, and any workflow that relies on it has to be tightened at the operator layer rather than the derivation layer. Practical responses include pinning the Nix binary itself, treating `--option sandbox-paths` and `extra-sandbox-paths` as security-relevant configuration rather than convenience flags, and preferring `ca-derivations` or fully content-addressed setups for any build where hermeticity is actually the goal. The post is also a reminder that hermeticity is a property of a whole system, and that Nix's default model was always repeatability rather than bit-for-bit reproducibility, with the sandbox setting sitting exactly on the seam between those two ideas.
Written by elseif from the cluster below · checked for specifics the sources never containedTHE CLUSTER