Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0b49d96
docs: add dendritic pattern architecture documentation
workflow Jan 30, 2026
5565191
feat(dendritic): add flake-parts bootstrap infrastructure
workflow Jan 30, 2026
558fc13
feat(dendritic): migrate first batch of apps to dendritic pattern
workflow Jan 30, 2026
e9756e1
refactor(dendritic): flatten structure to match original pattern
workflow Jan 30, 2026
2b20e05
feat(dendritic): migrate more features to dendritic pattern
workflow Jan 30, 2026
f522a43
feat(dendritic): migrate neovim with 43 plugin sub-modules
workflow Jan 30, 2026
0b2bee2
feat(dendritic): migrate complex features (niri, theming, audio, fish…
workflow Jan 30, 2026
bb4b326
feat(dendritic): bulk migrate apps and desktop features (batch 1)
workflow Jan 30, 2026
3025c7b
feat(dendritic): bulk migrate apps and desktop features (batch 2)
workflow Jan 30, 2026
e96399b
feat(dendritic): migrate system features (bluetooth, virtualisation)
workflow Jan 30, 2026
e962e3b
feat(dendritic): bulk migrate hardware, desktop, and app features (ba…
workflow Jan 31, 2026
9d893cf
feat(dendritic): migrate security features (yubico, security, nushell)
workflow Jan 31, 2026
d9f0a1e
feat(dendritic): migrate batch of apps modules (obs, kubernetes-tools…
workflow Jan 31, 2026
17b79ad
feat(dendritic): migrate core system modules (boot, networking, users…
workflow Jan 31, 2026
9e40f50
feat(dendritic): add nix-settings module
workflow Jan 31, 2026
56f5959
feat(dendritic): add hosts composition layer (Phase 4 foundation)
workflow Jan 31, 2026
1976b73
refactor(dendritic): fix dendrix options and cleanup old structure
workflow Jan 31, 2026
ceb6254
fix(dendritic): make GPU modules conditional on dendrix options
workflow Jan 31, 2026
e16760a
refactor(secrets): switch to dendritic pattern as well
workflow Jan 31, 2026
372a4ed
feat(dendritic): add missing home-manager modules
workflow Jan 31, 2026
0fc26bc
fix(dendritic): make btrfs and ddc-backlight conditional
workflow Jan 31, 2026
1bb77a4
fix(dendritic): update secrets
workflow Jan 31, 2026
a5123bd
fix(dendritic): comments and icons that disappeared in the migration
workflow Jan 31, 2026
18ae4f6
fix(dendritic): restore remaining comments and LF icons from migration
workflow Feb 3, 2026
555bca1
fix(dendrites): update secrets
workflow Feb 3, 2026
667a6ba
fix(impermanence): remove duplicate persistence paths
workflow Feb 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 14 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
# Architecture

This configuration follows the [dendritic pattern](doc/DENDRITIC.md). Key points:

- All modules live in `parts/`
- Each feature file configures both NixOS and home-manager aspects together
- Host-specific values are in top-level config options (`config.dendrix.*`), not `specialArgs`
- Features own their persistence paths

# Bash commands

- `nh os switch`: build and switch to new NixOS generation (including home-manager)
- `nh os build`: build only

# Code style

## Shell Scripts in Nix
Use `pkgs.writeShellApplication`, provide all necessary `runtimeInputs` and move the script into its own file inside the <module>/scripts/ dir.

Use `pkgs.writeShellApplication`, provide all necessary `runtimeInputs` and move the script into its own file inside the `<feature>/scripts/` dir.

# Workflow

- Instead of searching the web for NixOS or home-manager options, use `man configuration.nix` for NixOS and `man home-configuration.nix` for the home-manager manual locally.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,18 @@ But here's a rough guide:

![nix-valley-of-doom](assets/nix-valley-of-despair.png)

## Notes
## Architecture

This configuration follows the [dendritic pattern](doc/DENDRITIC.md) using flake-parts.

## Upgrades

- [Upgrade Checklist](doc/upgrades/Checklist.md)
- [NixOS 24.11 Upgrade Adventures](doc/upgrades/2411/NixOS-24.11.md)
- [NixOS 24.05 Upgrade Adventures](doc/upgrades/2405/NixOS-24.05.md)
- [NixOS Upgrade Checklist](doc/upgrades/Checklist.md)

## Notes

- [Moving an Existing Installation to a new Disk](doc/MOVING.md)

## Acknowledgements
Expand Down
7 changes: 0 additions & 7 deletions configuration.nix

This file was deleted.

250 changes: 250 additions & 0 deletions doc/DENDRITIC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
# Dendritic Pattern Architecture

This document describes the architecture of this NixOS configuration, which follows the [dendritic pattern](https://github.com/mightyiam/dendritic).

## Core Principles

1. **Aspect-oriented**: Each file configures a single feature across all relevant configuration classes (NixOS, home-manager)
2. **Top-level modules**: All files are flake-parts modules imported into a unified evaluation
3. **Co-location**: Related NixOS and home-manager config live together in one file
4. **Automatic imports**: Files in `parts/` are auto-imported; adding a feature = creating a file
5. **No specialArgs pass-thru**: Host-specific values live in top-level config options, accessible to all modules

## Directory Structure

```
nixos-config/
├── flake.nix # Minimal: inputs + import-tree ./parts
├── flake.lock
├── parts/ # All flake-parts modules (auto-imported)
│ ├── modules.nix # Imports flake-parts.flakeModules.modules
│ ├── hosts.nix # Host definitions, dendrix options, and feature composition
│ │
│ ├── _hosts/ # Host-specific config (underscore excludes from import-tree)
│ │ ├── flexbox/
│ │ │ ├── default.nix # Host-specific NixOS settings
│ │ │ └── hardware-scan.nix
│ │ └── numenor/
│ │ ├── default.nix
│ │ └── hardware-scan.nix
│ │
│ ├── features/ # Feature modules
│ │ ├── core/ # Essential system features
│ │ │ ├── boot.nix
│ │ │ ├── networking.nix
│ │ │ ├── users.nix
│ │ │ ├── nix-settings.nix
│ │ │ └── locale.nix
│ │ │
│ │ ├── hardware/ # Hardware-specific features
│ │ │ ├── nvidia.nix # Driver + env vars + home config
│ │ │ ├── amd.nix
│ │ │ ├── audio.nix # PipeWire + home audio tools
│ │ │ └── bluetooth.nix
│ │ │
│ │ ├── desktop/ # Desktop environment
│ │ │ ├── niri.nix # Compositor + keybindings + scripts
│ │ │ ├── waybar.nix
│ │ │ ├── dunst.nix
│ │ │ ├── fuzzel.nix
│ │ │ └── theming.nix # Stylix + specialisations
│ │ │
│ │ ├── dev/ # Development tools
│ │ │ ├── neovim/ # Complex features can be directories
│ │ │ │ ├── default.nix
│ │ │ │ ├── lsp.nix
│ │ │ │ └── plugins/
│ │ │ ├── git.nix
│ │ │ ├── jujutsu.nix
│ │ │ └── claude-code.nix
│ │ │
│ │ ├── apps/ # Applications
│ │ │ ├── firefox.nix
│ │ │ ├── alacritty.nix
│ │ │ ├── fish.nix
│ │ │ └── ...
│ │ │
│ │ ├── services/ # System services
│ │ │ ├── docker.nix
│ │ │ ├── syncthing.nix
│ │ │ └── restic.nix
│ │ │
│ │ └── security/ # Security features
│ │ ├── sops.nix
│ │ ├── gpg.nix
│ │ └── keyring.nix
└── doc/
└── DENDRITIC.md
```

## Anatomy of a Feature File

A feature file configures all aspects of a single feature:

```nix
# parts/features/apps/alacritty.nix
{ ... }:
{
flake.modules.nixos.alacritty = { pkgs, ... }: {
fonts.packages = [ pkgs.fira-code ];
};

flake.modules.homeManager.alacritty = { pkgs, lib, osConfig, ... }: {
programs.alacritty = {
enable = true;
settings = {
font.normal.family = "FiraCode Nerd Font";
};
};

# Feature owns its persistence paths
# Use osConfig to access NixOS-level dendrix options from home-manager
home.persistence."/persist" = lib.mkIf osConfig.dendrix.isImpermanent {
directories = [ ".config/alacritty" ];
};
};
}
```

## Host-Specific Values via NixOS Options

Instead of threading `specialArgs` through module evaluations, host-specific values are defined as NixOS options (`config.dendrix.*`). These are defined in `parts/hosts.nix` and set for each host:

```nix
# parts/hosts.nix (excerpt)
{config, lib, inputs, ...}: let
# NixOS module that defines dendrix options
dendrixOptionsModule = {lib, ...}: {
options.dendrix = {
hostname = lib.mkOption { type = lib.types.str; };
isLaptop = lib.mkOption { type = lib.types.bool; default = false; };
isImpermanent = lib.mkOption { type = lib.types.bool; default = false; };
hasNvidia = lib.mkOption { type = lib.types.bool; default = false; };
hasAmd = lib.mkOption { type = lib.types.bool; default = false; };
stateVersion = lib.mkOption { type = lib.types.str; };
homeStateVersion = lib.mkOption { type = lib.types.str; };
};
};

mkHost = { hostname, hostModule, dendrixConfig, ... }:
nixpkgs.lib.nixosSystem {
modules = [
dendrixOptionsModule
{ dendrix = dendrixConfig; } # Set values for this host
# ... other modules
];
};
in {
flake.nixosConfigurations = {
flexbox = mkHost {
hostname = "flexbox";
hostModule = ./_hosts/flexbox;
dendrixConfig = {
hostname = "flexbox";
isLaptop = true;
hasNvidia = true;
# ...
};
};
};
}
```

Feature modules access these values from NixOS config:

```nix
# parts/features/hardware/nvidia.nix - NixOS modules use config.dendrix.*
{ ... }:
{
flake.modules.nixos.nvidia = { config, lib, ... }: {
hardware.nvidia.enable = lib.mkIf config.dendrix.hasNvidia true;
};

# Home-manager modules use osConfig.dendrix.*
flake.modules.homeManager.nvidia = { osConfig, lib, ... }: {
home.sessionVariables = lib.mkIf osConfig.dendrix.hasNvidia {
__GLX_VENDOR_LIBRARY_NAME = "nvidia";
};
};
}
```

## Host Definition

Hosts are defined in `parts/hosts.nix` using a `mkHost` helper that composes all dendritic modules:

```nix
{ config, lib, inputs, ... }: let
# Collect all dendritic modules
nixosModules = builtins.attrValues config.flake.modules.nixos;
homeManagerModules = builtins.attrValues config.flake.modules.homeManager;

mkHost = { hostname, hostModule, dendrixConfig, ... }:
inputs.nixpkgs.lib.nixosSystem {
modules =
commonNixosModules # External modules (home-manager, stylix, etc.)
++ [ dendrixOptionsModule ]
++ nixosModules # All dendritic NixOS modules
++ [
hostModule # Host-specific hardware and settings
{ dendrix = dendrixConfig; }
{
home-manager.sharedModules = homeManagerModules;
}
];
};
in {
flake.nixosConfigurations = {
flexbox = mkHost {
hostname = "flexbox";
hostModule = ./_hosts/flexbox;
dendrixConfig = { hostname = "flexbox"; isLaptop = true; hasNvidia = true; /* ... */ };
};
numenor = mkHost {
hostname = "numenor";
hostModule = ./_hosts/numenor;
dendrixConfig = { hostname = "numenor"; isImpermanent = true; hasAmd = true; /* ... */ };
};
};
}
```

All dendritic modules are included in all hosts. Features use `config.dendrix.*` to conditionally enable host-specific behavior.

## Key Concepts

### flake-parts

[flake-parts](https://flake.parts) provides the module system for flake outputs. The `flake.modules.<class>.<name>` options use `deferredModule` types, allowing modules to be defined once and composed into multiple host configurations.

### Feature Ownership

Each feature owns all its concerns:
- System-level configuration (`flake.modules.nixos.*`)
- User-level configuration (`flake.modules.homeManager.*`)
- Persistence paths (declared within the home-manager module)
- Scripts (co-located in the feature file or a sibling `scripts/` directory)

### Specialisations

Theme switching is handled in `parts/features/desktop/theming.nix`:

```nix
flake.modules.nixos.theming = { ... }: {
stylix.base16Scheme = "gruvbox-dark-hard.yaml";

specialisation.light.configuration = {
stylix.base16Scheme = "catppuccin-latte.yaml";
stylix.polarity = "light";
};
};
```

## References

- [Dendritic pattern introduction (video)](https://youtu.be/-TRbzkw6Hjs)
- [mightyiam/dendritic](https://github.com/mightyiam/dendritic)
- [flake.parts documentation](https://flake.parts)
- [Doc-Steve/dendritic-design-with-flake-parts](https://github.com/Doc-Steve/dendritic-design-with-flake-parts)
Loading