nix-config/AGENTS.md

6.8 KiB

AGENTS.md — nix-config

Personal NixOS/nix-darwin configuration using flake-parts, import-tree, home-manager, and nixvim.


Repository Overview

flake.nix               # Thin entry: delegates to import-tree ./modules
Justfile                # Task runner (deploy, dry-run, update, gc, etc.)
modules/
  config.nix            # flake-parts options schema (flake.modules, flake.globalConfig)
  systems.nix           # Supported systems list
  hosts/
    dnsc-machine/       # Only active NixOS host (x86_64-linux)
  base/                 # macOS system defaults + NixOS locale/boot/user base
  <feature>/            # One directory per feature module
    default.nix         # Always the primary file
    _sub-module.nix     # Underscore-prefixed for large module splits

import-tree auto-discovers every .nix file under modules/no manual imports list in flake.nix.
New modules are picked up automatically; just create modules/<name>/default.nix.


Build / Validation Commands

There is no CI and no nix fmt/nix flake check target configured.

Goal Command
Deploy to host just deploy <host> (sudo nixos-rebuild switch --flake .#<host>)
Deploy with trace just debug <host> (adds --show-trace --verbose)
Dry-run build just dry <host> — evaluates the closure without realising it
Update all inputs just update (nix flake update)
Garbage collect just gc (sudo nix-collect-garbage --delete-old)
Wipe old profiles just clean (removes profiles older than 7 days)
NixOS REPL just repl

Only active host: dnsc-machine

just dry dnsc-machine   # fastest sanity check — no sudo required
just deploy dnsc-machine

There are no unit tests or checks outputs. Validation = dry-run build succeeds + manual deploy.

Linting tools available (installed as system packages): nil (LSP), statix (linter).
Run statix check . to lint Nix files for common anti-patterns.


Flake Architecture

inputs.flake-parts  →  mkFlake framework
inputs.import-tree  →  auto-loads modules/
inputs.nixpkgs      →  nixpkgs-unstable (channel tarball URL, not github:)
inputs.home-manager →  follows nixpkgs
inputs.nix-darwin   →  LnL7/nix-darwin master
inputs.nixvim       →  nix-community/nixvim
inputs.helium       →  custom browser flake
inputs.dms          →  DankMaterialShell (COSMIC alternative desktop)

Custom flake-parts options (defined in modules/config.nix):

  • flake.modules.nixos.<name> — NixOS system modules
  • flake.modules.darwin.<name> — nix-darwin system modules
  • flake.modules.homeManager.<name> — home-manager user modules
  • flake.globalConfig.{username,fullname,email} — shared personal values

Module Pattern

Every feature lives in modules/<feature>/default.nix and registers itself into the module registry.
Host configs (modules/hosts/<name>/default.nix) opt in by listing modules explicitly.

Standard tri-layer module

{ inputs, ... }:
{
  flake.modules.nixos.<name> =
    { pkgs, ... }:
    {
      # NixOS system-level config
      home-manager.sharedModules = [
        inputs.self.modules.homeManager.<name>
      ];
    };

  flake.modules.darwin.<name> =
    { pkgs, ... }:
    {
      # nix-darwin system-level config
      home-manager.sharedModules = [
        inputs.self.modules.homeManager.<name>
      ];
    };

  flake.modules.homeManager.<name> =
    { pkgs, config, ... }:
    {
      # home-manager user-level config
    };
}

System-only module (no HM layer)

{ ... }:
{
  flake.modules.nixos.<name> =
    { pkgs, ... }:
    {
      # NixOS config only, e.g. bluetooth, printing, gaming
    };
}

Sub-module splitting (for large modules like neovim)

flake.modules.homeManager.neovim =
  { pkgs, ... }:
  {
    imports = [
      ./_options.nix
      ./_lsp.nix
      ./_formatter.nix
    ];
  };

Host definition pattern

{ inputs, config, ... }:
let
  hostname = "dnsc-machine";
in
{
  flake.nixosConfigurations.dnsc-machine = inputs.nixpkgs.lib.nixosSystem {
    system = "x86_64-linux";
    modules = with config.flake.modules.nixos; [
      home-manager
      base
      shell
      # ... feature modules by name
      {
        imports = [ ./_hardware-configuration.nix ];
        networking.hostName = hostname;
        system.stateVersion = "24.11";
      }
    ];
  };
}

Code Style

Formatting

  • 2-space indentation throughout, no tabs
  • Opening brace on the same line as the attribute/binding
  • Closing brace on its own line
  • No trailing commas in attribute sets or lists (standard Nix)
  • Multi-line function arguments use the exploded form:
    {
      inputs,
      config,
      ...
    }:
    
    Short arg lists use the inline form: { pkgs, ... }:

Inline embedded languages

Use language comments to enable editor syntax highlighting inside multiline Nix strings:

interactiveShellInit = /* fish */ ''
  fish_vi_key_bindings insert
'';

extraConfig = /* lua */ ''
  vim.opt.number = true
'';

Naming conventions

Thing Convention Example
Module directories kebab-case cli-tools/, nvidia-graphics/
Primary module file default.nix every feature dir
Sub-module files _kebab-case.nix (underscore prefix) _lsp.nix, _formatter.nix
Hardware config _hardware-configuration.nix matches sub-module style
flake.modules.nixos.* keys kebab-case flake.modules.nixos.cli-tools
flake.modules.homeManager.* keys kebab-case name, homeManager namespace flake.modules.homeManager.shell
let bindings camelCase hmConfig, commonPackages
Host names kebab-case with owner prefix dnsc-machine, dnsc-server
Fish functions snake_case fish_greeting, tmux_sessionizer
Fish abbrs short lowercase lg, gg, g

Attribute ordering

In NixOS/HM options, follow upstream conventions:

  • enable = true; first
  • then primary config attributes
  • then nested option sets

No conditional feature flags

Feature modules do not use mkEnableOption/mkIf. Everything in a module is unconditionally enabled when that module is included in a host's modules list. Opt-in/opt-out happens at the host level by adding or removing the module name.


Adding a New Feature Module

  1. Create modules/<feature>/default.nix
  2. Define flake.modules.nixos.<feature> and/or flake.modules.homeManager.<feature>
  3. To activate on a host, add <feature> to the modules = with config.flake.modules.nixos; [ ... ] list in the host's default.nix
  4. No registration in flake.nix needed — import-tree picks it up automatically

Commit Style

Lowercase, brief, imperative — no conventional-commits prefix required:

add bluetooth module
fix cli tools config
remove gtk handling
update nixpkgs input