FAQ
What goes wrong without caisson that this fixes?
Caisson addresses major failure patterns seen in poly-flake setups:
First, input explosion: a flake-parts module that uses inputs.foo
works only if every downstream flake also pulls in foo. This ends
up meaning that each level of a dependency tree has to re-mention all
of its transitive dependencies, either directly or in the form of a
bunch of “follow” pins, or else you get an explosion of flake
versions.
Second, collisions: Overlays that write top-level attributes tend to fight over the same flat namespace.
What is closure-inputs, and who sets it?
closure-inputs is the sources your flake passes to mkLib: its
pinned inputs, under their input names and with no self.
inherit (caisson.lib.caisson.pins.flake inputs) sources root; in the
mkLib call passes them. The caisson framework threads it in:
mkLibOverlay and mkModule apply it to each registered file as the
file’s first argument list. A file always receives the sources of the
flake that registered it: an overlay or module consumed from another
flake sees the sources of the flake it came from, not those of the
flake consuming it.
Closed inputs is the full convention.
What happens when two overlays define the same thing?
Composition is done via an ordered pass across the specified overlays, traversing dependencies in a depth-first, postfix manner. For attrSets and lists, following the conventions results in a merge. For atomic attributes, contentions mean that the later overlay’s definition wins. I say “convention” because overlays are actually capable of addressing their predecessor directly, so they can technically implement whatever merging logic they deem appropriate.
Can I adopt this incrementally in an existing flake-parts flake?
Yes. lib.caisson.flake-parts.mkTopConfiguration wraps the
mkFlake of flake-parts, and plain
flake-parts modules work unchanged. It is recommended to start by
composing a library with lib.caisson.mkLib. You can hand your
existing top-level module to mkTopConfiguration and let the conventions
spread file by file from there.
What does lib.caisson.flake-parts.mkModule do to my module?
A few things:
- applies the closure argument list, so the module can use
closure-inputs - records the file’s path as
_file, for better error messages - gives path-registered modules a deduplication
key.
Do I pin nixpkgs, home-manager, and the rest myself?
Yes, you manage the ecosystem pins in your own flake, which is the
point: a caisson integration is glue over the ecosystem’s evaluator,
composing with whatever ecosystem source version you give it, so
caisson imposes no transitive pins and two consumers of the same
caisson revision can run different nixpkgs revisions. Version skew
within your ecosystems lives in your locks, where you can see and
manage it. A flake that uses one version of an ecosystem everywhere
can set a flake-level default at mkLib, a function of sources
(defaultEcosystemSrc = sources: { nixos = sources.nixpkgs; };). It
is also possible to pass ecosystemSrc, also a function of
sources, explicitly on each caisson call. The explicit argument
wins, and a source named exactly like the key is the final fallback
(handy for leaf nodes).
What does this cost at evaluation time?
Measurements show: relatively little. The eval-weight harness runs in CI and holds caisson’s overhead (a minimal caisson consumer minus a raw flake-parts flake) to ceilings on deterministic counters: thunks, values, allocations, and full nixpkgs, nixpkgs-lib, and module-system evaluation counts. The overhead is orders of magnitude below a single nixpkgs evaluation. Evaluation weight documents the harness and the current numbers.
How mature is this?
Pre-release. At the time of writing, it is used heavily by the author and by no one else.