Flake compatibility and versioning

Coming from a more programming language and less package management background, I have a different biases on compatibility to nix. However with flakes exporting library symbols, this all feels very similar, and we seem to be entering a world where diamond dependencies once again become a problem.

On the performance side, while this seems to come out of a desire for perfect compatibility and reproducibility and solves diamond dependency problems for shared libraries and binaries, it means that we have a 1000 instances of nixpkgs and complaints about “why am I compiling four different rust compilers just to get one python package”.

Finally, it would be nice to have a single way of signalling to users (and nix flake update) that they can I safely upgrade a flake. Currently, some have chosen semver, others seasonal incompatible releases, and others rolling releases.

One potential solutions follows:

First, we would need to agree on a way to expose a flake version string, say outputs.version.

Next we can choose to define semantics for what it means. I have a strong preference for “just do semver”, it has flaws, but is quite powerful. Alternatively, we could simply do a string compare, and request that users change the value when compatibility is broken, similar to what nixpkgs does.

Finally, we could introduce a new flag for “I want the latest version of every compatible version of a given flake”. (I am assuming vcs could be used to define what “latest” is.) This would effectively be syntax sugar for builtins.mapAttrs (_: input: input.inputs.yyy.follows = yyy) inputs for all compatible versions of yyy.

In semver terms, this would mean you would only need one input for every major version, or in nixpkgs terms one input for every seasonal release.

Note, since this breaks reproducibility guarantees just like inputs.follows, it could cause two working flake inputs to break their parent. Fortunately, this will only result from a bug or broken compatibility promise, and most other languages have decided that version ranges [recte sat solvers] or just pushing the upstream author to fix the bug, are good enough. However, since this changes the user experience, I can understand that this may be too critical a promise to walk back, even if it saves a few GB of /nix/store or hours of compile time.

This topic came up regarding the forthcoming openssl vuln.

It is pretty easy to ensure that you have the latest version of nixpkgs, and nixpkgs.legacyPackages.${system}.openssl is up to date, but ensuring that all your flakes depend on a version of nixpkgs with the fix is harder (and all the .follows required to fix it, will make inputs a mess). And even when this work is done, the problem follows you any time you add a new dependency or a new vuln pops, as you must ensure that nothing you depend on introduces a vulnerable thing into your tree.

It seems much easier to enforce a flat flake namespace (or at least an opt-in feature) that would allow you to trivially say “all nixpkgs are up to date”.