Configuration

pynix reads a default for the options that repeat across the commands, so a profile does not have to state them on each invocation.

Where a value comes from

Four layers. The first one that names a value wins:

  1. the flag on the command line

  2. the environment

  3. $XDG_CONFIG_HOME/pynix/config.toml

  4. the built-in default

$ pynix build --store local ...        # the flag
$ PYNIX_STORE=daemon pynix build ...   # the environment

XDG_CONFIG_HOME defaults to ~/.config. PYNIX_CONFIG names another file, for a user who keeps more than one profile. A file that is not there is not an error.

The file

[defaults]
store = "daemon"
verbosity = "notice"
print-build-logs = true

[nix]
substituters = ["https://cache.nixos.org/", "https://mine.example/"]
trusted-public-keys = ["cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="]
max-jobs = 8

[defaults] holds the options of pynix itself. Each key is the option without the leading dashes, and the environment variable is PYNIX_ with the name in capitals: store is PYNIX_STORE.

[nix] holds the Nix settings, under the names that nix.conf uses. The environment variable is PYNIX_NIX_ with the name in capitals: max-jobs is PYNIX_NIX_MAX_JOBS.

A setting that takes more than one value takes a TOML array, and it also takes the nix.conf spelling:

[nix]
substituters = "https://cache.nixos.org/ https://mine.example/"

The second spelling is the one that an environment variable needs, because a variable carries one string:

$ export PYNIX_NIX_SUBSTITUTERS='https://cache.nixos.org/ https://mine.example/'
$ export PYNIX_NIX_ACCESS_TOKENS='github.com=<token>'

Turning off an option that the file turns on

print-build-logs is a flag, so a file that sets it to true would leave no way to turn it off. Each such option has a negative form:

$ pynix build --no-print-build-logs ...

The two variables a completion reads

A Tab evaluates Nix, so it has a budget. These two are read from the environment alone: they are not options of the command, they have no entry in the configuration file, and they do not go through the settings model. A completion runs on every keypress that ends in Tab, and a settings tree costs more than the number is worth.

variable

default

what it does

PYNIX_COMPLETION_BUDGET

5.0

Seconds a completion may take. When it runs out, the completion offers nothing, and the shell shows what it shows when no program answers.

PYNIX_COMPLETION_DEBUG

unset

A file name. A completion that fails writes its traceback there.

Raise the budget when you complete against something large and you would rather wait:

$ export PYNIX_COMPLETION_BUDGET=15

A completion that answers nothing looks the same whatever went wrong. That is deliberate -- a traceback would land in the middle of your command line -- and it means a defect here is invisible. PYNIX_COMPLETION_DEBUG is the way to look.

The budget does not stop a flake input that never answers. The fetch runs below the layer that the budget cancels, so a flake with an unreachable input outlasts it. Issue #231 holds that.

What a Tab for --flake reads

Before the #, --flake completes a flake reference, and it reads the same three sources nix reads: the bare ., the directories under what you have typed, and every layer of the flake registry.

The registry can download. The global layer names a URL in the flake-registry setting, and Nix fetches it. That is what nix does on the same keypress, and the result is cached for tarball-ttl seconds, so only the first Tab of an hour pays for it. Measured: 0.54 s warm, 4.10 s with no network and an expired cache, and Nix answers from the stale copy in that case rather than failing.

A Tab downloads once, where a command downloads five times. Nix retries a download download-attempts times with a backoff, waits connect-timeout seconds for each, and lets a connected transfer go silent for stalled-download-timeout seconds. The defaults are 5, 15 s and 300 s, and each of them outlasts a keypress. A completion sets them to 1, 3 s and 3 s. Only a completion reads these values; a real command keeps the patient ones.

Measured. With no network, the registry call gives up after 4.646 s at the defaults and after 0.002 s at one attempt, and the first figure is over the budget. Against a socket that accepts the connection and then never writes, the call outlasted 25 s at the default stall timeout and gave up after 3.004 s at three seconds.

Those three bound what Nix fetches with curl, which is the registry and any tarball. A git+https: flake input runs git as a separate process, which reads none of them, so a completion sets git's own pair as well: GIT_HTTP_LOW_SPEED_LIMIT=1 and GIT_HTTP_LOW_SPEED_TIME=2. If you have set either yourself, yours is kept.

Measured against a server that accepts the connection and then writes nothing, completing a flake whose one input names it: at git's defaults the completion outlasted 120 s and had to be killed, and with the pair it answered nothing after 4.6 s and left no git process behind. Two seconds and not three, because Nix retries the fetch once and the budget is five.

A Tab still completes with no network, and under nix it does not. getRegistries builds all four registry layers before it returns any of them, so nix throws away your /etc/nix/registry.json and your own registry.json whenever it cannot reach the global one. Measured on a machine that pins nixpkgs in its system registry: nix build nixp<TAB> with an unreachable registry offers nothing at all. pynix asks again without the global layer and offers what the local ones hold.

flake-registry in your nix.conf reaches this program. It did not until issue #234. Nix keeps four settings registries, and nix.conf fills in only what is registered with globalConfig; libcmd is what registers the other three, and nanopynix does not link libcmd. So every fetch setting came from the caller and none from the file, and flake-registry was absent from nanopynix.list_settings().

nanopynix now registers one object of each kind, which is what src/libcmd/common-eval-args.cc does. Each call still builds its own settings, and it starts from what the file said rather than from the compiled default. A value you pass still wins over the file, as it does in nix.

What pynix build does not need

nix build writes a result symlink, which is a GC root, and it prints nothing unless it is told to. pynix build creates no symlink and no root, and it prints the outputs as JSON. There is no --no-link to configure, because there is no link.

To make a root, ask for one: pynix store add-root <path> <link>.