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 modulesflake.modules.darwin.<name>— nix-darwin system modulesflake.modules.homeManager.<name>— home-manager user modulesflake.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:
Short arg lists use the inline form:{ inputs, config, ... }:{ 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
- Create
modules/<feature>/default.nix - Define
flake.modules.nixos.<feature>and/orflake.modules.homeManager.<feature> - To activate on a host, add
<feature>to themodules = with config.flake.modules.nixos; [ ... ]list in the host'sdefault.nix - No registration in
flake.nixneeded —import-treepicks 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