Lix 2.90 makes failures visible while preserving Nix expressions
Lix's first release starts from Nix 2.18 and concentrates on reliability. Its compatibility choices distinguish expressions, commands and native library consumers.
A setting silently ignored by a build daemon can leave the client believing it requested one policy while the daemon applied another. Lix 2.90 changes that interaction. When a daemon rejects a client setting because the client lacks trusted-user status, it emits a warning. Making that decision visible is a useful example of the reliability work behind the fork's first release.
The 10 July announcement calls the release Vanilla Ice Cream. Lix starts from Nix 2.18, backports fixes from later upstream versions and aims to preserve previously valid Nix code, including flakes. The release's argument is largely about making the implementation and its tools more predictable, rather than introducing an incompatible expression language.
I value Nix because a declared system can be rebuilt from its inputs. That makes implementation reliability a practical concern in its own right. A configuration can state the right intention while a silent failure in the package manager makes the resulting operation difficult to explain.
Compatibility has several distinct meanings
The 2.90 release notes document intentional breaking changes as well as preserved behaviour. That is compatible with the announcement's aim of accepting valid Nix expressions. Command-line options and native library interfaces are separate contracts from the expression language.
The C++ libraries change names from forms such as libnixexpr to liblixexpr, and headers move under Lix-specific paths. The notes explicitly describe this as forcing downstream consumers to handle Lix deliberately. It avoids accidentally treating the fork as a newer upstream version through compatibility code that only compares version numbers.
The builtins make a related choice. builtins.nixVersion returns the fixed string 2.18.3-lix, and builtins.langVersion returns 6, matching the stated 2.18 language basis. Lix's own release number is larger, but it does not imply that every feature in a later upstream Nix version exists in Lix.
That is a concrete reason to detect a required builtin directly when possible. An implementation version encodes release history, which diverges after a fork. Feature detection asks whether the capability the expression needs actually exists. The release notes recommend that approach rather than treating the number as a universal capability ranking.
Rejected settings now have an observable result
The trusted-setting warning illustrates a broader concern in the release. A daemon has authority over the build and can reject options supplied by an untrusted client. That restriction is meaningful, but the client also needs to know the request was rejected. The old behaviour only emitted a debug message.
Lix promotes that information to a warning for each ignored setting. The security boundary remains in place. The difference is that a caller no longer has to infer rejection from a later result or reproduce the operation with debugging enabled.
The announcement lists similar changes around remote-builder connection failures and daemon crashes. A failed connection now prints its error, and a daemon crash is identified as a likely explanation. Those are improvements in the description of failure, without requiring a claim that every failed operation now succeeds.
That distinction matters in a distributed build environment. A derivation failing to build and a client failing to reach a builder require different investigations. An error report that preserves the boundary between them reduces ambiguity about which operation actually failed.
Evaluation errors are evaluated again
Lix also removes the familiar cached-failure error. According to the release notes, an expression recorded as a failure in the evaluation cache is evaluated again. That allows the current failure to appear directly without disabling the cache merely to see its explanation.
A cached failure is a statement about an earlier evaluation result. The developer usually needs the current reason the expression cannot be evaluated. Re-evaluating that case makes the error report useful at the moment of investigation, even when successful evaluation results remain worth caching.
The same emphasis appears in the REPL. Its printer chooses a readable single-line or multiline representation instead of always producing a long line. Type errors include the value that caused them, and debugger improvements preserve source positions and access to bindings. These changes concern the information available while understanding a configuration.
The announcement also extends :doc to lambdas. A user can inspect documentation for a library function in the REPL instead of limiting that operation to builtins. The announcement's example asks for documentation of lib.forEach, connecting an expression under inspection to its documented intent.
I find that more useful than a generic promise of a friendlier interface. It names an operation available during evaluation and the kind of function it can explain. Library-heavy configurations need tools that understand the functions users actually call.
Locking and updating become distinct commands
The release changes the command interface around flake inputs. nix flake lock creates a lock file or adds missing inputs, while nix flake update updates inputs. Supplying input names to the latter limits the update to those inputs.
nix flake update nixpkgs
That command replaces the earlier update-input use described in the release notes. Its name states the requested mutation directly. The lock operation can preserve already locked inputs without sharing an interface whose flags also request updating them.
The notes remove the old --update-input and --recreate-lock-file flags from installable commands. This is an intentional command compatibility change. A working Nix expression does not imply that every script invoking the old flags will continue unchanged, which is another reason to distinguish the language claim from the CLI contract.
Profiles also move to human-readable element names and stop accepting indices. Their manifest format upgrades on modification, after which older versions cannot use the profile. That is a state-format boundary with consequences separate from evaluating the source that supplied a package.
A fixed registry trades automatic changes for stability
Lix deprecates online flake registries and vendors a copy of the default registry. The release notes explain that the online mapping was not pinned and could change its targets. Fetching it also caused offline failures, including cases where a local reference existed.
Vendoring the mapping gives that default a fixed content for the release and allows offline use. It also means a change to the online registry does not silently change the release's default indirections. A caller can reason about the mapping that shipped with the implementation instead of depending on its current network response.
That choice addresses name resolution rather than claiming to replace every flake lock. The registry maps references, while a project's input selection has its own role. The benefit is removing an unpinned online dependency from that default lookup path.
The release notes also fix nested flake-input overrides that previously did nothing despite being documented. A declaration aimed at a dependency further down the input tree can now reach that dependency. This is an important complement to a compatibility claim. Preserving the accepted syntax is insufficient when the implementation ignores the request it expresses. The user needs the override to have its documented effect, especially when it is intended to keep several transitive dependencies on a common Nixpkgs input. The fix addresses that particular discrepancy without requiring every intermediate flake to expose every dependency itself.
Reliability work includes the development process
The release builds exclusively with Meson and removes the old Make-based build system. Its announcement connects that work to compilation and tooling, including an immediately usable clangd configuration. The notes say downstream packaging that did not use Nix needs to be rewritten for the new build arrangement.
Lix also enables checks that can turn internal bugs into process aborts, including signed-overflow trapping and standard-library assertions. The notes explicitly classify such aborts as bugs to report. A harder failure is useful to development when it identifies invalid internal behaviour, but it should not be mistaken for a guarantee that users will never encounter a crash.
The announcement's evaluation-speed claim is 5 to 20 percent relative to 2.18, depending on the benchmark. That qualification matters. It is the project's reported comparison, not a measurement of an unspecified user's configuration. The more directly inspectable improvements are the changed error paths and the named operations the tools now support.
For an existing Nix user, the first release therefore offers a combination of expression compatibility and deliberate changes in the surrounding implementation. The warning for a rejected setting is a particularly clear result. The daemon still controls the policy, and the client can now see when its requested setting did not become part of that policy.