4.7 KiB
Nix Conventions
This document records local Nix style and design conventions.
Use these conventions when changing this flake.
Read this document with docs/dictionary.md.
Names
Reserve lib for nixpkgs.lib
Use lib only for the base nixpkgs.lib value.
Do not use lib for project functions.
Use util for library functions passed through module arguments.
Use flake.mk<Name> for assembly functions used by flake outputs.
Avoid:
specialArgs = {
lib = util;
};
Use:
specialArgs = {
inherit util;
};
Then call library functions through util inside modules:
{
lib,
util,
...
}:
{
networking.networkmanager.unmanaged = [ "interface-name:${util.bridgeName 10}" ];
}
Name project function namespaces by role
Put pure project functions under util.
Put functions that create systems or output values under flake.
This keeps data-only functions separate from functions that carry flake context.
Avoid:
util.mkHosts { root = self + "/hosts"; }
Use:
flake.mkHosts { root = self + "/hosts"; }
File Placement
Put files in the directory that owns their role
Choose the file location before writing a Nix file.
Put assembly files under flake/.
Put library files under lib/.
Put modules under modules/.
Put host configurations under hosts/<name>/.
Put host-side guest wiring under guests/.
Put registry definitions under registry/.
Keep flake.nix focused on inputs and output wiring.
Do not add root-level Nix files other than flake.nix.
A module is self-contained configuration under modules/.
A module configures one program, service, or subsystem.
Files under hosts/, guests/, and modules/ return NixOS modules.
Use default.nix for module directories with support files
Use default.nix as the primary module when a module directory owns support files.
Keep sibling files for data or code read by that primary module.
Avoid:
modules/shell/fish/fish.nix
modules/shell/fish/config.fish
Use:
modules/shell/fish/default.nix
modules/shell/fish/config.fish
Use non-module default.nix files only for directory assembly
Outside modules/, use default.nix to connect files in its directory.
Do not define primary assembly functions in default.nix.
Put primary assembly functions in named files.
Avoid:
flake/default.nix # defines mkHost
Use:
flake/default.nix # imports ./host.nix
flake/host.nix # defines mkHosts
Keep assembly files focused on one concept
Give each assembly file under flake/ one top-level concept.
Keep helper functions private to that file when no other file needs them.
Export the constructor for the concept when another assembly file needs it.
Name that constructor mk<Concept>.
Avoid:
{
inherit collectNixFiles mkHost mkHosts;
}
Use:
{
inherit mkHosts;
}
Function Arguments
Use closed interfaces for assembly files and library files
Use exact function argument patterns for assembly files and library files. The caller owns the argument set for these files. The imported file names each accepted attribute. The imported file rejects stale or accidental extra attributes.
Avoid:
{
inputs,
lib,
self,
...
}:
Use:
{
inputs,
lib,
self,
}:
Pass function arguments explicitly
Pass only the values an assembly file or library file needs.
Bind lib to the base nixpkgs.lib value at the call site.
Use inherit only for values that keep the same name.
Avoid:
import ./flake inputs
Use:
let
inherit (nixpkgs) lib;
in
import ./flake { inherit lib inputs self; }
Keep NixOS module interfaces open
Files under hosts/, guests/, and modules/ return NixOS modules.
nixosSystem owns their argument shape.
Name each argument the module uses.
Keep ... so the module accepts standard arguments and special arguments.
Avoid:
{
config,
lib,
pkgs,
registry,
}:
Use:
{
config,
lib,
pkgs,
registry,
...
}:
let Bindings
Use role-specific binding names
A let binding name must explain the value's role in the expression.
Avoid names that describe only the value's type.
Avoid:
let
names = lib.attrNames hosts;
in
lib.genAttrs names mkHost
Use:
let
hostNames = lib.attrNames hosts;
in
lib.genAttrs hostNames mkHost
Standard Library Calls
Qualify standard library calls in flake assembly
Use qualified standard library calls inside assembly files.
Use lib.mapAttrs instead of inheriting mapAttrs from lib.
This keeps the source of each function visible.
Avoid:
let
inherit (lib) mapAttrs;
in
mapAttrs f attrs
Use:
lib.mapAttrs f attrs