4.4 KiB
AGENTS.md
Build Commands
Local Development
nix run .#build # Build current host config
nix run .#build -- <hostname> # Build specific host (chidi, janet, michael, tahani)
nix run .#apply # Build and apply locally (darwin-rebuild/nixos-rebuild switch)
nix flake check # Validate flake
Remote Deployment (NixOS only)
nix run .#deploy # Deploy to all NixOS hosts
nix run .#deploy -- .#michael # Deploy to specific NixOS host
nix run .#deploy -- .#tahani # Deploy to specific NixOS host
When you're on tahani and asked to apply, that means running nix run .#deploy.
Formatting
alejandra . # Format all Nix files
Code Style
Formatter
- Tool: Alejandra
- Config:
alejandra.tomlspecifies tabs for indentation - Command: Run
alejandra .before committing
File Structure
- Modules:
modules/- All configuration (flake-parts modules, auto-imported by import-tree)hosts/- Per-host composition modulesprofiles/- Shared host and user profile bundles_lib/- Utility functions (underscore = ignored by import-tree)_darwin/- Darwin-specific sub-modules_neovim/- Neovim plugin configshosts/_parts/- Host-specific leaf files (disk-config, hardware, service fragments, etc.)
- Apps:
apps/- Per-system app scripts (Nushell) - Secrets:
secrets/- SOPS-encrypted secrets (.sops.yamlfor config)
Architecture
Framework: den (vic/den) — every .nix file in modules/ is a flake-parts module
Pattern: Feature/aspect-centric, not host-centric
Aspects: den.aspects.<name>.<class> where class is:
nixos- NixOS-only configurationdarwin- macOS-only configurationhomeManager- Home Manager configurationos- Applies to both NixOS and darwin
Hosts: den.hosts.<system>.<name> declared in modules/inventory.nix
Profiles: shared bundles live under modules/profiles/{host,user} and are exposed as den.aspects.host-* and den.aspects.user-*
Defaults: den.default.* defined in modules/defaults.nix
Imports: Auto-imported by import-tree; underscore-prefixed dirs (_lib/, _darwin/, etc.) are excluded from auto-import
Deployment: deploy-rs for NixOS hosts (michael, tahani); darwin hosts (chidi, janet) are local-only
Nix Language Conventions
Function Arguments:
{inputs, pkgs, lib, ...}:
Use ... to capture remaining args. Let Alejandra control the exact layout.
Attribute Sets:
den.aspects.myfeature.os = {
enable = true;
config = "value";
};
One attribute per line with trailing semicolons.
Lists with Packages:
with pkgs;
[
age
alejandra
ast-grep
]
Use with pkgs; for package lists, one item per line.
Aspect Definition:
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.den.aspects.myfeature;
in {
options.den.aspects.myfeature = {
enable = mkEnableOption "Feature description";
};
config = mkIf cfg.enable {
# configuration
};
}
- Use
with lib;for brevity with NixOS lib functions - Define
cfgfor config options - Use
mkIf,mkForce,mkDefaultappropriately
Conditional Platform-Specific Code:
++ lib.optionals stdenv.isDarwin [
_1password-gui
dockutil
]
++ lib.optionals stdenv.isLinux [
lm_sensors
]
Naming Conventions
- Aspect names:
den.aspects.<name>.<class>for feature configuration - Hostnames: Lowercase, descriptive (e.g.,
michael,tahani,chidi,janet) - Module files: Descriptive, lowercase with hyphens (e.g.,
neovim-config.nix)
Secrets Management
- Use SOPS for secrets (see
.sops.yaml) - Never commit unencrypted secrets
- Secret definitions live in per-host modules (
modules/hosts/michael.nix,modules/hosts/tahani.nix, etc.) - Shared SOPS defaults (module imports, key paths) in
modules/secrets.nix
Aspect Composition
Use den.aspects.<name>.includes to compose aspects:
den.aspects.myfeature.includes = [
"other-aspect"
"another-aspect"
];
Key Conventions
- No
specialArgs— den batteries handle input passing - No hostname string comparisons in shared aspects
- Host-specific config goes in
den.aspects.<hostname>.* - Shared config uses
osclass (applies to both NixOS and darwin) - Non-module files go in
_-prefixed subdirs