nix-tree makes store dependency exploration interactive
The new terminal browser follows store-path dependencies, helping explain closure size without confusing a dependency graph with reclaimable disk space.
A package's own directory can be small while the paths it depends on occupy much more space. Finding the reason requires following dependencies beyond the first level. Utku Demir's new terminal browser makes that exploration interactive. Announced yesterday under the name nixdu, it is now named nix-tree, with the first package published under that name today.
The announcement thread describes its author's motivation as repeated investigation of transitive dependencies to reduce closure size. The 0.1.0.0 package record dates its upload to July 2. Its bundled README documents the initial interface, which is a better guide to today's tool than assumptions about what a dependency browser might eventually support.
A closure contains the dependencies beyond the first step
The Nix 2.3.6 store-query reference distinguishes immediate references from the full closure. Querying references gives a store path's direct dependencies. Querying requisites gives the closure, including the recursively required paths. These answer different questions even when both produce lists of names in the store.
A direct-dependency list can explain the first link without explaining the total consequence. One referenced path can itself require a substantial collection of other outputs. An investigation into size therefore needs to follow the relationship through the graph. The terminal browser addresses the interaction cost of doing that repeatedly, rather than introducing a new meaning of dependency into Nix.
The existing commands make the distinction explicit:
nix-store --query --references ~/.nix-profile
nix-store --query --requisites ~/.nix-profile
The manual permits queries through a symlink outside the store and applies them to its target. That makes a profile a useful starting point. The first command asks about its immediate references, while the second asks about everything required through those references. Neither command says which part of that dependency set is unique to the profile.
I am interested in declarative configuration partly because dependencies can be examined as concrete relationships. A browser that reduces the effort of following those relationships is useful operationally. It gives a maintainer another way to inspect the consequence of a package choice without hiding the underlying store model.
The initial interface is deliberately small
The first README accepts paths as arguments and defaults to the user's Nix profile and the current NixOS system path. Navigation uses arrow keys or the familiar hjkl keys. The documented w binding opens a why-depends mode, while a help binding exposes the controls. These are the capabilities documented for the initial package, without implying support for every kind of Nix project reference.
nix-tree ~/.nix-profile
This invocation focuses the browser on an explicit profile. Choosing the root matters because the resulting dependency graph describes that selected object. A system profile and a user profile can retain different sets of paths. Seeing a large dependency under one is evidence about that profile's relationship to it, rather than proof that every installed application needs it.
The initial installation documentation fetches the project's source archive and also supplies a Nixpkgs overlay. This gives users a route to the tool without claiming that the new package is already part of every stable Nixpkgs collection. An overlay introduces a package through a package-set customization. It is separate from a promise about which distribution channel includes the package by default.
Build-time and runtime roots produce different graphs
The Nix query reference also distinguishes a derivation's build-time dependency graph from an output path's runtime graph. Selecting the derivation explains what is required to construct an output. Selecting the output explains the references retained by that result. The same project can therefore have different relevant dependency sets depending on which object starts the query.
This distinction prevents a common misreading of development tools in a dependency list. A compiler required to build a program is not automatically a runtime dependency of the program's output. Conversely, an output can retain a reference that makes another path necessary even if the relationship was not obvious in the package's high-level description. A size investigation needs to identify which graph it is exploring.
The browser's purpose is to make following those relationships easier. It cannot decide from a package's name whether the reference is intentional. The relevant engineering decision remains in the package definition or output content that establishes the dependency. Interactive exploration helps locate the relationship that needs explanation before anyone changes it.
Nix already provides a textual tree query. Its reference says that it follows a path only on the first encounter, preventing a graph with shared dependencies from expanding repeatedly in the printed representation. That behavior illustrates why the word tree requires care here. The underlying relationship can include the same dependency through multiple parents. A navigable presentation organizes the graph for a reader, but it does not make each displayed branch own a separate copy of every dependency below it. Size reasoning still has to account for shared store paths.
Closure size and collectible space answer different questions
Nix garbage collection uses reachability from roots. The manual calls paths reachable from those roots live and describes paths outside that set as dead. A path can appear in one package's closure while also remaining reachable from another root. Removing the first package therefore does not imply that every byte in its closure becomes collectible.
The query interface includes referrers for the reverse direction. These are store paths currently referring to a selected path. Unlike the selected path's references, its set of referrers changes as other paths enter or leave the store. Looking backward through the current store is therefore a different operation from identifying the immutable dependency references of one output.
My preference is to preserve that distinction when discussing a large closure. Reducing a package's dependency set can improve its deployment requirements even if the local machine retains shared paths for other reasons. July's tool helps investigate the first problem through a navigable graph. The store's root and referrer queries supply the additional context needed to explain why a path remains present on a particular machine.