me like nix
1---
2name: dendritic-nix
3description: Use when the user asks about or wants to implement the "dendritic nix pattern", aspect-oriented Nix configuration, organizing Nix config by feature/aspect instead of hostname, or migrating to flake-parts modules. Also applies when discussing cross-platform Nix configs (NixOS + Darwin + Home Manager) in a single file.
4version: 1.0.0
5---
6
7# Dendritic Nix Pattern
8
9The dendritic pattern is an **aspect-oriented** approach to Nix configuration built on [flake-parts](https://flake.parts). Each `.nix` file provides configuration for the same **aspect** (feature/concern) across different configuration classes (NixOS, Darwin, Home Manager, etc.).
10
11It is a configuration **pattern** — not a library or framework.
12
13## Core Principle
14
15Instead of organizing by host (`hosts/mira/default.nix`, `hosts/framework16/default.nix`), organize by **feature**. A single file like `modules/ssh.nix` contains the NixOS, Darwin, and Home Manager config for SSH all in one place.
16
17## File Structure
18
19- No mandatory directory structure
20- Every `.nix` file is a **flake-parts module** — uniform semantics
21- Files are auto-loaded (e.g., via `vic/import-tree`)
22- Files with `/_` in their path are ignored by convention
23
24```
25modules/
26├── ssh.nix # SSH config across all platforms
27├── vim.nix # Editor config across all platforms
28├── vic.nix # User "vic" across all platforms
29└── desktop/
30 ├── basic.nix # Basic desktop features
31 └── advanced.nix # Advanced desktop features (incremental)
32```
33
34## Module Pattern
35
36Each file is a flake-parts module that defines config for multiple configuration classes:
37
38```nix
39{ inputs, config, ... }: let
40 # Shared values — replaces specialArgs
41 sharedPort = 2277;
42in {
43 flake.modules.nixos.aspect-name = {
44 # NixOS system configuration
45 };
46
47 flake.modules.darwin.aspect-name = {
48 # macOS system configuration
49 };
50
51 flake.modules.homeManager.aspect-name = {
52 # Home Manager user configuration
53 };
54
55 perSystem = { pkgs, ... }: {
56 # Per-system packages, devShells, etc.
57 };
58}
59```
60
61## Complete Example: SSH
62
63```nix
64# modules/ssh.nix
65{ inputs, config, ... }: let
66 scpPort = 2277;
67in {
68 flake.modules.nixos.ssh = {
69 services.openssh = {
70 enable = true;
71 ports = [ scpPort ];
72 };
73 networking.firewall.allowedTCPPorts = [ scpPort ];
74 };
75
76 flake.modules.darwin.ssh = {
77 # macOS built-in SSH server config
78 };
79
80 flake.modules.homeManager.ssh = {
81 # ~/.ssh/config, authorized_keys, etc.
82 };
83
84 perSystem = { pkgs, ... }: {
85 # Custom packages using SSH facilities
86 };
87}
88```
89
90## User Definition Example
91
92```nix
93# modules/vic.nix
94let
95 userName = "vic";
96in {
97 flake.modules.nixos.${userName} = {
98 users.users.${userName} = {
99 isNormalUser = true;
100 extraGroups = [ "wheel" ];
101 };
102 };
103
104 flake.modules.darwin.${userName} = {
105 system.primaryUser = userName;
106 };
107
108 flake.modules.homeManager.${userName} = { pkgs, lib, ... }: {
109 home.username = lib.mkDefault userName;
110 home.homeDirectory = lib.mkDefault (
111 if pkgs.stdenvNoCC.isDarwin
112 then "/Users/${userName}"
113 else "/home/${userName}"
114 );
115 home.stateVersion = lib.mkDefault "25.05";
116 };
117}
118```
119
120## Minimal flake.nix
121
122```nix
123{
124 inputs = {
125 flake-parts.url = "github:hercules-ci/flake-parts";
126 import-tree.url = "github:vic/import-tree";
127 # other inputs...
128 };
129 outputs = inputs:
130 inputs.flake-parts.lib.mkFlake { inherit inputs; }
131 (inputs.import-tree ./modules);
132}
133```
134
135## Dynamic Inputs (Optional)
136
137With `vic/flake-file`, inputs can be declared per-module:
138
139```nix
140# modules/home/vim.nix
141{ inputs, ... }: {
142 flake-file.inputs.nixvim.url = "github:nix-community/nixvim";
143 flake.modules.homeManager.vim = {
144 # config using inputs.nixvim
145 };
146}
147```
148
149## Key Advantages
150
1511. **Feature closures**: Everything needed for a feature lives in one file
1522. **No specialArgs**: Shared values use `let` bindings and flake-parts options
1533. **Uniform file semantics**: Every `.nix` file is a flake-parts module
1544. **Incremental features**: Add `feature/basic.nix` and `feature/advanced.nix` independently
1555. **Cross-platform**: NixOS, Darwin, and Home Manager config coexist naturally
156
157## Configuration Classes
158
159Common classes used in `flake.modules.<class>`:
160- `nixos` — NixOS system config
161- `darwin` — macOS system config
162- `homeManager` — Home Manager user config
163- `nixvim` — Editor config
164- Custom classes as needed
165
166## When Helping Users
167
168- When migrating an existing config: map each "feature" (SSH, users, desktop, etc.) to its own flake-parts module file
169- Each module should define config for all relevant classes in one place
170- Use `let` bindings for values shared across classes instead of `specialArgs`
171- The pattern does NOT require flakes — see `vic/dendritic-unflake` for non-flake usage
172- Tools like `vic/import-tree` and `vic/flake-file` are recommendations, not requirements