How does the sandbox choose what store paths are available?

at first i thought it was just any store path that appeared within the derivation, but that’s not the case¹:

fish-shell> nix-build --expr '(import <nixpkgs> {}).runCommandLocal "example1" {} "ls '(realpath ~/.nix-profile)' > $out"'
this derivation will be built:
  /nix/store/6d3bgmc69blrsdnq6wbg062ikvs7apbh-example1.drv
building '/nix/store/6d3bgmc69blrsdnq6wbg062ikvs7apbh-example1.drv'...
ls: cannot access '/nix/store/ysd7v8g957qwzvy3ww6zg1szpazld8ql-profile': No such file or directory

(examples done in fish shell because i’m not sure how to cleanly do the interpolations in bash)

it seems to have something to do with string interpolation/string contexts and path literals:

> nix-build --expr '(import <nixpkgs> {}).runCommandLocal "example2" {} "ls ${'(realpath ~/.nix-profile)'} > $out"'
this derivation will be built:
  /nix/store/g5w6k2rb7m5l6gqc078dzvwl082788g2-example2.drv
building '/nix/store/g5w6k2rb7m5l6gqc078dzvwl082788g2-example2.drv'...
/nix/store/456df02a6ykvi45jq3z9gi2mbsp799nz-example2

but, using path literals like this doesn’t expose the original path, but instead creates a new store path:

> nix-build --expr '(import <nixpkgs> {}).runCommandLocal "example3" {} "echo ${'(realpath ~/.nix-profile)'} > $out"'
this derivation will be built:
  /nix/store/1vg9fm5sfgng3c1yf7v2qc3ssn3svw6a-example3.drv
building '/nix/store/1vg9fm5sfgng3c1yf7v2qc3ssn3svw6a-example3.drv'...
/nix/store/zh20mwkz2d0p2gh5g78fsq3acynbwy0p-example3
> cat /nix/store/zh20mwkz2d0p2gh5g78fsq3acynbwy0p-example3
/nix/store/fiahi9rssaa03l7vm6ypskav7k3gilvs-ysd7v8g957qwzvy3ww6zg1szpazld8ql-profile

perhaps something like lib.toDerivation with nativeBuildInputs would work, but ideally i would like to use builtin.derivation instead of mkDerivation (yes, i’m well aware that mkDerivation is better in 99% of cases, but i’m not trying to build a package, i’m doing low-level nix stuff)

inputSrcs seems related, but that’s a property of a store derivation, not a derivation attribute set.

poking around with builtins.hasContext reveals that interpolating a package is not the same as interpolating the store path of that package.

if you interpolate a fake package, that will bypass the creation of a new store path.

> nix-build --expr '(import <nixpkgs> {}).runCommandLocal "example3" {} "echo ${{outPath = "'(realpath ~/.nix-profile)'";}} > $out"'

this derivation will be built:
  /nix/store/mqlhyjhbkkdxk5brkndv6jg2kyd7r1yf-example3.drv
building '/nix/store/mqlhyjhbkkdxk5brkndv6jg2kyd7r1yf-example3.drv'...
/nix/store/bzgc47r5xys0hpc6w9gyk2il621p18ya-example3
> cat /nix/store/bzgc47r5xys0hpc6w9gyk2il621p18ya-example3
/nix/store/ysd7v8g957qwzvy3ww6zg1szpazld8ql-profile

Yup, you’re exploring the nuances of string context!

This blog post might be helpful to you: Understanding Nix's String Context | Shea's site

2 Likes

that still doesn’t explain the actual logic used by the sandbox.

i’m getting confusing results when builder is a store path created by a path literal.

Sorry, I don’t know what you mean by that. Are you referring to one of your above examples? If a path literal is interpolated into a string, then (1) the path is first copied into the store (if it’s already in the store, it’ll be copied into a new path, as you’ve observed), and (2) that new store path will be added to the string context and eventually included in the sandbox. Was that your question?

after some testing, it seems that paths added by nix-store --add are not visible in the sandbox, even if they are part of the context

> mkdir /tmp/emptydir
> nix-build --expr '(import <nixpkgs> {}).runCommandLocal "example4" {} "ls ${{outPath = "'(nix-store --add /tmp/emptydir)'";}} > $out"'

That looks like a string literal, not a path literal. Uninterpolated string literals don’t have a context. No offense, but this is covered in the post I linked.

it’s a fake derivation (that contains a string literal) interpolated into a string literal. the previous example i posted uses the same syntax, and works.

the string context sub-issue has been understood and resolved, but the main issue has not: how does the sandbox know what store paths to include?

even when a path is listed under inputSrcs, it is not always available. sometimes, the executable is available, but its runtime dependancies are not.

Example3 would not work if you actually tried to access the path with ls as you do in example4.

it sure would be nice if there was some reference documentation that explained how this works.

no, a 6 year old blogpost with basic errors (such as confusing ++ and +) is not reference documentation.