Modular, context-aware and aspect-oriented dendritic Nix configurations. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ den.oeiuwq.com
configurations den dendritic nix aspect oriented

more examples

+120 -8
+40 -7
README.md
··· 174 174 ``` 175 175 176 176 ```nix 177 - # custom user-defined Nix classes. 177 + # Custom Nix classes. 178 + 179 + # Example: A class for role-based configuration between users and hosts 180 + 181 + roleClass = 182 + { host, user }: 183 + { class, aspect-chain }: 184 + den._.forward { 185 + each = lib.intersectLists (host.roles or []) (user.roles or []); 186 + fromClass = lib.id; 187 + intoClass = _: host.class; 188 + intoPath = _: [ ]; 189 + fromAspect = _: lib.head aspect-chain; 190 + }; 191 + 192 + den.ctx.user.includes = [ roleClass ]; 193 + 194 + den.hosts.x86_64-linux.igloo = { 195 + roles = [ "devops" "gaming" ]; 196 + users = { 197 + alice.roles = [ "gaming" ]; 198 + bob.roles = [ "devops" ]; 199 + }; 200 + }; 201 + 202 + den.aspects.alice = { 203 + # enabled when host supports gaming role 204 + gaming = { pkgs, ... }: { programs.steam.enable = true; }; 205 + 206 + # enabled when host supports devops role 207 + devops = { pkgs, ... }: { virtualisation.podman.enable = true; }; 208 + }; 209 + ``` 210 + 211 + ```nix 212 + # Forward guards allow feature-detection without mkIf/mkMerge cluttering. 178 213 179 - # any aspect can use my `persys` class to forward configs into 180 - # nixos.environment.persistance."/nix/persist/system" 181 - # **ONLY** when environment.persistance option is present at host. 214 + # Aspects use the `persys` class without any conditional. And guard guarantees 215 + # settings are applied **only** when impermanence module has been imported. 182 216 persys = { host }: den._.forward { 183 217 each = lib.singleton true; 184 218 fromClass = _: "persys"; 185 219 intoClass = _: host.class; 186 220 intoPath = _: [ "environment" "persistance" "/nix/persist/system" ]; 187 221 fromAspect = _: den.aspects.${host.aspect}; 188 - guard = { options, ... }: options ? environment.persistance; 222 + guard = { options, config, ... }: options ? environment.persistance; 189 223 }; 190 224 191 225 # enable on all hosts 192 226 den.ctx.host.includes = [ persys ]; 193 227 194 - # becomes nixos.environment.persistance."/nix/persist/system".hideMounts = true; 195 - # no mkIf, set configs and guard ensures to include only when Impermanence exists 228 + # aspects just attach config to custom class 196 229 den.aspects.my-laptop.persys.hideMounts = true; 197 230 ```
+35 -1
docs/src/content/docs/guides/custom-classes.mdx
··· 153 153 }; 154 154 ``` 155 155 156 + #### Example: Role based configuration between users and hosts 157 + 158 + A dynamic class for matching roles between users and hosts. 159 + 160 + ```nix 161 + roleClass = 162 + { host, user }: 163 + { class, aspect-chain }: 164 + den._.forward { 165 + each = lib.intersectLists (host.roles or []) (user.roles or []); 166 + fromClass = lib.id; 167 + intoClass = _: host.class; 168 + intoPath = _: [ ]; 169 + fromAspect = _: lib.head aspect-chain; 170 + }; 171 + 172 + den.ctx.user.includes = [ roleClass ]; 173 + 174 + den.hosts.x86_64-linux.igloo = { 175 + roles = [ "devops" "gaming" ]; 176 + users = { 177 + alice.roles = [ "gaming" ]; 178 + bob.roles = [ "devops" ]; 179 + }; 180 + }; 181 + 182 + den.aspects.alice = { 183 + # enabled when host supports gaming role 184 + gaming = { pkgs, ... }: { programs.steam.enable = true; }; 185 + 186 + # enabled when host supports devops role 187 + devops = { pkgs, ... }: { virtualisation.podman.enable = true; }; 188 + }; 189 + ``` 190 + 156 191 #### Example: A git class that forwards to home-manager. 157 192 158 193 ```nix ··· 195 230 196 231 # enable class for all users: 197 232 den.ctx.user.includes = [ nixClass ]; 198 - 199 233 200 234 # custom aspect that uses the `nix` class. 201 235 nix-allowed = { user, ... }: { nix.allowed-users = [ user.userName ]; };
+45
templates/ci/modules/features/forward-from-custom-class.nix
··· 161 161 } 162 162 ); 163 163 164 + test-pair-of-hosts = denTest ( 165 + { 166 + den, 167 + lib, 168 + igloo, 169 + iceberg, 170 + ... 171 + }: 172 + let 173 + forwarded = 174 + { host, user }: 175 + { class, aspect-chain }: 176 + den._.forward { 177 + each = lib.optional (lib.elem host.name [ 178 + "igloo" 179 + "iceberg" 180 + ]) user; 181 + fromClass = _: "iced"; 182 + intoClass = _: host.class; 183 + intoPath = _: [ ]; 184 + fromAspect = _: lib.head aspect-chain; 185 + }; 186 + in 187 + { 188 + den.hosts.x86_64-linux.igloo.users.tux = { }; 189 + den.hosts.x86_64-linux.iceberg.users.tux = { }; 190 + 191 + den.aspects.igloo.homeManager.home.stateVersion = "25.11"; 192 + den.ctx.default.includes = [ forwarded ]; 193 + 194 + den.aspects.tux = { 195 + iced.networking.hostName = "iced"; 196 + }; 197 + 198 + expr = [ 199 + igloo.networking.hostName 200 + iceberg.networking.hostName 201 + ]; 202 + expected = [ 203 + "iced" 204 + "iced" 205 + ]; 206 + } 207 + ); 208 + 164 209 }; 165 210 }