This repository has been archived on 2026-08-30. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
dotfiles/docs/nix.md

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