# Nix Conventions This document records local Nix style and design conventions. Prefer these conventions when changing this flake. ## Names ### Reserve `lib` for `nixpkgs.lib` Use `lib` only for the base `nixpkgs.lib` value. Do not use `lib` for project helper functions. Use `dotfiles.lib` for pure project helpers passed through module arguments. Use `dotfiles.` for project assembly helpers passed through module arguments. Avoid: ```nix { lib = dotfilesHelpers; } ``` Use: ```nix { lib = nixpkgs.lib; dotfiles.lib = dotfilesHelpers; } ``` ## Imports ### Use closed interfaces for helper imports Use exact function argument patterns when an imported helper file has a closed interface. A helper file has a closed interface when the importer owns the argument set. The imported file must name each accepted attribute. This rejects stale or accidental extra attributes. Avoid: ```nix { inputs, lib, self, ... }: ``` Use: ```nix { inputs, lib, self, }: ``` ### Pass import arguments explicitly Pass only the values an imported helper file needs. Bind `lib` to the base `nixpkgs.lib` value at the call site. Use `inherit` for values that keep the same name. Avoid: ```nix import ./flake inputs ``` Use: ```nix import ./flake { inherit inputs self; lib = nixpkgs.lib; } ``` ### Keep NixOS module interfaces open NixOS modules must accept extra arguments because the module system owns their argument shape. Name the arguments the module uses. Keep `...` so the module accepts standard module arguments and special arguments. Avoid: ```nix { config, lib, pkgs, registry, }: ``` Use: ```nix { config, lib, pkgs, registry, ... }: ``` ### Use `@` for forwarding wrappers Use `@` when a wrapper must pass the complete argument set to another module function. Name the arguments the wrapper uses before `...`. Forward the bound argument set unchanged. Avoid: ```nix { dotfiles, ... }: dotfiles.guest { name = "sample"; } { inherit dotfiles; } ``` Use: ```nix args@{ dotfiles, ... }: dotfiles.guest { name = "sample"; } args ``` ## `let` Bindings ### Use `let` for important intermediate values Use a `let` binding when it names an important intermediate value. Use a `let` binding when it avoids repeating a complex expression. Do not use a `let` binding only to shorten a standard library call. Avoid: ```nix let inherit (lib) mapAttrs; in mapAttrs f attrs ``` Use: ```nix lib.mapAttrs f attrs ``` ## Standard Library Calls ### Qualify standard library calls in flake assembly Prefer qualified standard library calls inside flake assembly code. Use `lib.mapAttrs` instead of inheriting `mapAttrs` from `lib`. This keeps the source of helper functions visible. Avoid: ```nix let inherit (lib) mapAttrs; in mapAttrs f attrs ``` Use: ```nix lib.mapAttrs f attrs ``` ## Flake Assembly ### Keep assembly code under `flake/` Keep flake assembly code under `flake/`. Keep `flake.nix` focused on inputs and public outputs. A public output is any attribute returned by the flake `outputs` function. Another flake can reference a public output through `inputs..`. Tools such as `nix flake show` also display public outputs. Avoid: ```nix # flake.nix let mkHosts = hostsDir: ...; in { nixosConfigurations = mkHosts (self + "/hosts"); } ``` Use: ```nix # flake.nix let flakeAssembly = import ./flake { inherit inputs self; lib = nixpkgs.lib; }; in { nixosConfigurations = flakeAssembly.mkHosts (self + "/hosts"); } ``` ### Expose only supported flake outputs Use public outputs for the supported interface of this flake. Do not expose assembly helpers as public outputs unless an external consumer needs them. Keep internal helpers in `flake/` or `dotfiles` special arguments. Avoid: ```nix { lib = flakeAssembly; nixosConfigurations = flakeAssembly.mkHosts (self + "/hosts"); } ``` Use: ```nix { nixosConfigurations = flakeAssembly.mkHosts (self + "/hosts"); } ```