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

batteries included (#15)

move defaults

fixing hm

fixing

authored by oeiuwq.com and committed by

GitHub 445c67fe fb490adb

+496 -256
+1 -1
.github/workflows/test.yml
··· 34 34 _module.args.ci-os = "${{matrix.os}}"; 35 35 } 36 36 EOF 37 - nix run .#write-flake 37 + nix run .#write-flake --override-input den "github:$GITHUB_REPOSITORY/$GITHUB_SHA" 38 38 nix flake update den 39 39 nix run .#write-flake 40 40 nix flake metadata
+21 -19
README.md
··· 13 13 14 14 <img width="400" height="400" alt="den" src="https://github.com/user-attachments/assets/af9c9bca-ab8b-4682-8678-31a70d510bbb" /> 15 15 16 - - focused on host/home definitions. 17 - - host/home configs via aspects. 16 + - focused on host/home [definitions](#basic-usage). 17 + - host/home configs via [aspects](#advanced-aspect-patterns). 18 18 - multi-platform, multi-tenant hosts. 19 19 - shareable-hm in os and standalone. 20 20 - extensible for new host/home classes. 21 - - stable/unstable input channels. 21 + - stable/unstable input [channels](#custom-factories-instantiate). 22 22 - customizable os/home factories. 23 + - [batteries](modules/aspects/batteries) included and replaceable. 24 + - features [tested](https://github.com/vic/den/actions) with [examples](templates/default/modules/_example). 23 25 24 26 **❄️ Try it now! Launch our template VM:** 25 27 ··· 163 165 164 166 This library also provides `default` aspects to apply global configurations to all hosts, users, or homes of a certain class. 165 167 166 - - `den.aspects.default.host`: Applied to all hosts. 167 - - `den.aspects.default.user`: Applied to all users within hosts. 168 - - `den.aspects.default.home`: Applied to all standalone homes. 168 + - `den.default.host`: Applied to all hosts. 169 + - `den.default.user`: Applied to all users within hosts. 170 + - `den.default.home`: Applied to all standalone homes. 169 171 170 172 ## Advanced Customization 171 173 ··· 262 264 263 265 You can define default settings that apply to all hosts, users, or homes. This is a powerful way to enforce global standards and reduce duplication. 264 266 265 - ##### Class-Based Defaults (`den.aspects.default.<host|user|home>`) 267 + ##### Class-Based Defaults (`den.default.<host|user|home>`) 266 268 267 269 You can apply settings to all systems of a specific *class* (e.g., `nixos`, `darwin`, `homeManager`) by adding them directly to the default aspect. 268 270 269 271 ```nix 270 272 # modules/aspects.nix 271 273 { 272 - den.aspects.default = { 274 + den.default = { 273 275 host.nixos.system.stateVersion = "25.11"; 274 276 host.darwin.system.stateVersion = 6; 275 277 user.homeManager.home.stateVersion = "25.11"; ··· 278 280 } 279 281 ``` 280 282 281 - ##### Parametric Defaults (`den.aspects.default.<host|user|home>.includes`) 283 + ##### Parametric Defaults (`den.default.<host|user|home>.includes`) 282 284 283 285 For more dynamic configurations, you can add *functions* to the `includes` list of a default aspect. These functions are called for every host, user, or home, and receive the corresponding object (`host`, `user`, or `home`) as an argument. This allows you to generate configuration that is parameterized by the system's properties. 284 286 ··· 290 292 { 291 293 # 1. Define a parametric aspect (a function) that takes a host and returns 292 294 # a configuration snippet. 295 + # re-usable aspects use `den.aspects` and private ones let bindings. 293 296 den.aspects.example.provides.hostName = { host }: { class, ... }: { 294 297 ${class}.networking.hostName = host.hostName; 295 298 }; 296 299 297 300 # 2. Include this function in the default host includes. 298 301 # This function will now be called for every host defined in `den.hosts`. 299 - den.aspects.default.host.includes = [ 302 + den.default.host.includes = [ 300 303 den.aspects.example.provides.hostName 301 304 ]; 302 305 } ··· 304 307 305 308 ###### How Parametric Defaults Work 306 309 307 - Under the hood, `aspects.default.host`, `aspects.default.user`, and `aspects.default.home` are not static aspects but **functors**. When `den` evaluates a system, it invokes the corresponding default functor, which in turn iterates over the functions in its `includes` list. It calls each function with a context-specific object and merges the resulting configuration snippets. 310 + Under the hood, `den.default.host`, `den.default.user`, and `den.default.home` are not static aspects but **functors**. When `den` evaluates a system, it invokes the corresponding default functor, which in turn iterates over the functions in its `includes` list. It calls each function with a context-specific object and merges the resulting configuration snippets. 308 311 309 312 The parameters passed to the functions in each `includes` list are as follows: 310 313 311 - - `den.aspects.default.host.includes`: Each function receives the `host` object (`{ host }`). 312 - - `den.aspects.default.user.includes`: Each function receives the `host` and `user` objects (`{ host, user }`). This applies to users defined within a host. 313 - - `den.aspects.default.home.includes`: Each function receives the `home` object (`{ home }`). This applies to standalone home-manager configurations. 314 + - `den.default.host.includes`: Each function receives the `host` object (`{ host }`). 315 + - `den.default.user.includes`: Each function receives the `host` and `user` objects (`{ host, user }`). This applies to users defined within a host. 316 + - `den.default.home.includes`: Each function receives the `home` object (`{ home }`). This applies to standalone home-manager configurations. 314 317 315 318 This mechanism allows you to create highly reusable and context-aware default configurations that adapt to each system's specific attributes. 316 319 ··· 323 326 { 324 327 den.aspects.example.provides.user = { user, host }: 325 328 let 326 - # Default configuration for a user 327 - defaultConfig = { 329 + aspect = { 328 330 nixos.users.users.${user.userName}.isNormalUser = true; 329 331 darwin.system.primaryUser = user.userName; 330 332 }; 331 333 332 - # Special configuration for NixOS-on-WSL 333 - hostSpecificConfig.adelie = { 334 + # Special aspect for NixOS-on-WSL 335 + per-host.adelie = { 334 336 nixos.defaultUser = user.userName; 335 337 }; 336 338 in 337 339 # Use the host-specific config if it exists, otherwise use the default. 338 - hostSpecificConfig.${host.name} or defaultConfig; 340 + per-host.${host.name} or aspect; 339 341 } 340 342 ``` 341 343
+1 -1
flake.nix
··· 5 5 path = ./templates/default; 6 6 description = "Minimal nixos configuration"; 7 7 }; 8 - packages = import ./nix/default_packages.nix; 8 + packages = import ./nix/template-packages.nix; 9 9 }; 10 10 }
+7
modules/aspects/_aspect_option.nix
··· 1 + { inputs, lib }: 2 + description: 3 + lib.mkOption { 4 + inherit description; 5 + default = { }; 6 + type = (inputs.flake-aspects.lib lib).types.aspectSubmodule; 7 + }
+78
modules/aspects/batteries/home-manager.nix
··· 1 + { 2 + inputs, 3 + lib, 4 + den, 5 + ... 6 + }: 7 + let 8 + home-manager.description = '' 9 + integrates home-manager into nixos/darwin OS classes. 10 + 11 + usage: 12 + 13 + for using home-manager in just a particular host: 14 + 15 + den.aspects.my-host.includes = [ (den.home-manager { host = den.hosts.<system>.my-host; }) ]; 16 + 17 + for enabling home-manager by default on all hosts: 18 + 19 + den.default.host.includes = [ den.home-manager ]; 20 + 21 + Does nothing for hosts that have no users with `homeManager` class. 22 + Expects `inputs.home-manager` to exist. If `<host>.hm-input` exists 23 + it is the name of the input to use instead of `home-manager`. 24 + 25 + For each user resolves den.aspects.''${user.aspect} and imports its homeManager class module. 26 + ''; 27 + 28 + home-manager.__functor = 29 + _: 30 + { host }: 31 + { class, aspect-chain }: 32 + let 33 + hmUsers = builtins.filter (u: u.class == "homeManager") (lib.attrValues host.users); 34 + 35 + hmUserModule = 36 + user: 37 + den.aspects.${user.aspect}.resolve { 38 + inherit aspect-chain; 39 + class = "homeManager"; 40 + }; 41 + 42 + users = map (user: { 43 + name = user.userName; 44 + value.imports = [ (hmUserModule user) ]; 45 + }) hmUsers; 46 + 47 + hmModule = inputs.${host.hm-input or "home-manager"}."${class}Modules".home-manager; 48 + osPerUser = 49 + user: 50 + let 51 + homeDir = if lib.hasSuffix "darwin" host.system then "/Users" else "/home"; 52 + in 53 + { 54 + users.users.${user.userName} = { 55 + name = lib.mkDefault user.userName; 56 + home = lib.mkDefault "${homeDir}/${user.userName}"; 57 + }; 58 + }; 59 + 60 + aspect.${class} = { 61 + imports = [ hmModule ] ++ (map osPerUser hmUsers); 62 + home-manager.users = lib.listToAttrs users; 63 + }; 64 + 65 + supportedHmOS = builtins.elem class [ 66 + "nixos" 67 + "darwin" 68 + ]; 69 + enabled = supportedHmOS && builtins.length hmUsers > 0; 70 + in 71 + if enabled then aspect else { }; 72 + 73 + aspect-option = import ../_aspect_option.nix { inherit inputs lib; }; 74 + in 75 + { 76 + config.den = { inherit home-manager; }; 77 + options.den.home-manager = aspect-option "home-managed OS"; 78 + }
+62
modules/aspects/batteries/import-tree.nix
··· 1 + { 2 + inputs, 3 + lib, 4 + den, 5 + ... 6 + }: 7 + let 8 + import-tree.description = '' 9 + an aspect that recursively imports non-dendritic .nix files from a `_''${class}` directory. 10 + 11 + this can be used to help migrating from huge existing setups, 12 + by having files: path/_nixos/*.nix, path/_darwin/*.nix, etc. 13 + 14 + requirements: 15 + - inputs.import-tree 16 + 17 + usage: 18 + 19 + this aspect can be included explicitly on any aspect: 20 + 21 + # example: my-host will import _nixos or _darwin nix files automatically. 22 + den.aspects.my_host.includes = [ (den.import-tree ./.) ]; 23 + 24 + or it can be default imported per host/user/home: 25 + 26 + 27 + # each host will import-tree from ./hosts/''${host.name}/_{nixos,darwin}/*.nix 28 + den.default.host.includes = [ (den.import-tree._.host ./hosts) ]; 29 + 30 + # each user will import-tree from ./users/''${user.name}@''${host.name}/_homeManager/*.nix 31 + den.default.user.includes = [ (den.import-tree._.user ./users) ]; 32 + 33 + # each home will import-tree from ./homes/''${home.name}/_homeManager/*.nix 34 + den.default.home.includes = [ (den.import-tree._.home ./homes) ]; 35 + 36 + you are also free to create your own auto-imports layout following the implementation of these. 37 + ''; 38 + 39 + import-tree.__functor = 40 + _: root: 41 + { class, ... }: 42 + let 43 + path = "${toString root}/_${class}"; 44 + aspect.${class}.imports = [ 45 + (inputs.import-tree path) 46 + ]; 47 + in 48 + if builtins.pathExists path then aspect else { }; 49 + 50 + import-tree.provides = { 51 + host = root: { host }: import-tree "${toString root}/${host.name}"; 52 + user = root: { host, user }: import-tree "${toString root}/${user.name}@${host.name}"; 53 + home = root: { home }: import-tree "${toString root}/${home.name}"; 54 + }; 55 + 56 + aspect-option = import ../_aspect_option.nix { inherit inputs lib; }; 57 + 58 + in 59 + { 60 + config.den = { inherit import-tree; }; 61 + options.den.import-tree = aspect-option "import-tree aspects"; 62 + }
+96
modules/aspects/defaults.nix
··· 1 + { inputs, lib, ... }: 2 + let 3 + 4 + # set host static default values directly by class: 5 + # 6 + # den.aspects.default.host = { 7 + # nixos = ...; 8 + # darwin = ...; 9 + # } 10 + # 11 + # or register a function that takes the { host } param: 12 + # 13 + # den.aspects.default.host.includes = [ aspectByHost ]; 14 + # aspectByHost = { host }: { class, aspect-chain }: { 15 + # nixos = ...; 16 + # darwin = ...; 17 + # } 18 + default.host = 19 + { aspect, ... }: 20 + { 21 + __functor = 22 + _: 23 + { host }: 24 + { class, ... }: 25 + { 26 + name = "(default.host ${host.name})"; 27 + includes = map (f: f { inherit host; }) aspect.includes; 28 + ${class} = aspect.${class} or { }; 29 + }; 30 + }; 31 + 32 + # set user static values directly by class: 33 + # 34 + # den.aspects.default.user = { 35 + # nixos = ...; 36 + # darwin = ...; 37 + # homeManager = ...; 38 + # } 39 + # 40 + # or register a function that takes the { host, user } param: 41 + # 42 + # den.aspects.default.user.includes = [ aspectByUser ]; 43 + # aspectByUser = { host, user }: { class, aspect-chain }: { 44 + # nixos = ...; 45 + # darwin = ...; 46 + # homeManager = ...; 47 + # } 48 + default.user = 49 + { aspect, ... }: 50 + { 51 + __functor = 52 + _: 53 + { host, user }: 54 + { class, ... }: 55 + { 56 + name = "(default.user ${host.name} ${user.name})"; 57 + includes = map (f: f { inherit host user; }) aspect.includes; 58 + ${class} = aspect.${class} or { }; 59 + }; 60 + }; 61 + 62 + # set home static values directly by class: 63 + # 64 + # den.aspects.default.home = { 65 + # homeManager = ...; 66 + # } 67 + # 68 + # or register a function that takes the { home } param: 69 + # 70 + # den.aspects.default.home.includes = [ aspectByHome ]; 71 + # aspectByHome = { home }: { class, aspect-chain }: { 72 + # homeManager = ...; 73 + # } 74 + default.home = 75 + { aspect, ... }: 76 + { 77 + __functor = 78 + _: 79 + { home }: 80 + { class, ... }: 81 + { 82 + name = "(default.home ${home.name})"; 83 + includes = map (f: f { inherit home; }) aspect.includes; 84 + ${class} = aspect.${class} or { }; 85 + }; 86 + }; 87 + 88 + aspect-option = import ./_aspect_option.nix { inherit inputs lib; }; 89 + 90 + in 91 + { 92 + config.den = { inherit default; }; 93 + options.den.default.host = aspect-option "host defaults"; 94 + options.den.default.user = aspect-option "host user defaults"; 95 + options.den.default.home = aspect-option "standalone home defaults"; 96 + }
+76
modules/aspects/dependencies.nix
··· 1 + # create aspect dependencies from hosts/users 2 + { 3 + lib, 4 + config, 5 + den, 6 + ... 7 + }: 8 + let 9 + hosts = lib.flatten (map builtins.attrValues (builtins.attrValues config.den.hosts)); 10 + homes = lib.flatten (map builtins.attrValues (builtins.attrValues config.den.homes)); 11 + 12 + # creates den.aspects.${host.aspect} 13 + # 14 + # ${host.aspect} depends on: 15 + # - den.default.host and its includes list taking { host } 16 + hostAspect = host: { 17 + ${host.aspect} = { 18 + includes = [ (den.default.host { inherit host; }) ]; 19 + ${host.class} = { }; 20 + }; 21 + }; 22 + 23 + # creates aspects.${user.aspect} 24 + # 25 + # ${user.aspect} depends on: 26 + # - den.default.user and its includes list taking { host, user } 27 + # 28 + # ${host.aspect} depends on: 29 + # - aspects.${user.aspect}.provides.${host.aspect} { host, user } 30 + # - aspects.${user.aspect}.provides.hostUser { host, user } 31 + # - den.default.user.provides.hostUser { host, user } 32 + hostUserAspect = 33 + host: user: 34 + { aspects, ... }: 35 + let 36 + context = { inherit host user; }; 37 + empty = 38 + # deadnix: skip 39 + { host, user }: _: { }; 40 + in 41 + { 42 + ${user.aspect} = { 43 + ${user.class} = { }; 44 + includes = [ (den.default.user context) ]; 45 + }; 46 + 47 + ${host.aspect}.includes = [ 48 + ((aspects.${user.aspect}.provides.${host.aspect} or empty) context) 49 + ((aspects.${user.aspect}.provides.hostUser or empty) context) 50 + ((den.default.user.provides.hostUser or empty) context) 51 + ]; 52 + }; 53 + 54 + # creates den.aspects.${home.aspect} 55 + # 56 + # ${home.aspect} depends on: den.default.home 57 + homeAspect = home: { 58 + ${home.aspect} = { 59 + includes = [ (den.default.home { inherit home; }) ]; 60 + ${home.class} = { }; 61 + }; 62 + }; 63 + 64 + hostDeps = map (host: [ 65 + (hostAspect host) 66 + (map (hostUserAspect host) (builtins.attrValues host.users)) 67 + ]) hosts; 68 + 69 + homeDeps = map homeAspect homes; 70 + 71 + deps = hostDeps ++ homeDeps; 72 + 73 + in 74 + { 75 + config.den.aspects = lib.mkMerge (lib.flatten deps); 76 + }
+14
modules/options.nix
··· 1 + { 2 + inputs, 3 + lib, 4 + config, 5 + ... 6 + }: 7 + let 8 + types = import ./_types.nix { inherit inputs lib config; }; 9 + in 10 + { 11 + options.den.hosts = types.hostsOption; 12 + options.den.homes = types.homesOption; 13 + config._module.args.den = config.den; 14 + }
-128
nix/aspects.nix
··· 1 - # create aspect dependencies from hosts/users 2 - { 3 - lib, 4 - config, 5 - inputs, 6 - ... 7 - }: 8 - let 9 - hosts = lib.flatten (map builtins.attrValues (builtins.attrValues config.den.hosts)); 10 - homes = lib.flatten (map builtins.attrValues (builtins.attrValues config.den.homes)); 11 - 12 - hostAspect = 13 - host: 14 - { aspects, ... }: 15 - { 16 - ${host.aspect} = { 17 - includes = [ (aspects.default.host { inherit host; }) ]; 18 - ${host.class} = { }; 19 - }; 20 - }; 21 - 22 - homeAspect = 23 - home: 24 - { aspects, ... }: 25 - { 26 - ${home.aspect} = { 27 - includes = [ (aspects.default.home { inherit home; }) ]; 28 - ${home.class} = { }; 29 - }; 30 - }; 31 - 32 - emptyHostUserProvider = 33 - # deadnix: skip 34 - { host, user }: _: { }; 35 - 36 - hostUserAspect = 37 - host: user: 38 - { aspects, ... }: 39 - let 40 - context = { inherit host user; }; 41 - genericProvider = aspects.${user.aspect}.provides.hostUser or emptyHostUserProvider; 42 - userProvider = aspects.${user.aspect}.provides.${host.aspect} or genericProvider; 43 - in 44 - { 45 - ${user.aspect} = { 46 - ${user.class} = { }; 47 - includes = [ (aspects.default.user context) ]; 48 - }; 49 - 50 - ${host.aspect}.includes = [ (userProvider context) ]; 51 - }; 52 - 53 - hostDeps = map (host: [ 54 - (hostAspect host) 55 - (map (hostUserAspect host) (builtins.attrValues host.users)) 56 - ]) hosts; 57 - 58 - homeDeps = map homeAspect homes; 59 - 60 - deps = hostDeps ++ homeDeps; 61 - 62 - defaults = [ 63 - { 64 - default.host = 65 - { aspect, ... }: 66 - { 67 - __functor = 68 - _: 69 - { host }: 70 - { class, ... }: 71 - { 72 - name = "(default.host ${host.name})"; 73 - includes = map (f: f { inherit host; }) aspect.includes; 74 - ${class} = aspect.${class} or { }; 75 - }; 76 - }; 77 - default.user = 78 - { aspect, ... }: 79 - { 80 - __functor = 81 - _: 82 - { host, user }: 83 - { class, ... }: 84 - { 85 - name = "(default.user ${host.name} ${user.name})"; 86 - includes = map (f: f { inherit host user; }) aspect.includes; 87 - ${class} = aspect.${class} or { }; 88 - }; 89 - }; 90 - default.home = 91 - { aspect, ... }: 92 - { 93 - __functor = 94 - _: 95 - { home }: 96 - { class, ... }: 97 - { 98 - name = "(default.home ${home.name})"; 99 - includes = map (f: f { inherit home; }) aspect.includes; 100 - ${class} = aspect.${class} or { }; 101 - }; 102 - }; 103 - } 104 - ]; 105 - 106 - aspect-types = (inputs.flake-aspects.lib lib).types; 107 - defaultOption = 108 - description: 109 - lib.mkOption { 110 - inherit description; 111 - default = { }; 112 - type = aspect-types.aspectSubmodule; 113 - }; 114 - 115 - in 116 - { 117 - config.den.aspects = lib.mkMerge (lib.flatten (defaults ++ deps)); 118 - 119 - options.den.aspects.default = lib.mkOption { 120 - description = "defaults"; 121 - default = { }; 122 - type = lib.types.submodule { 123 - options.host = defaultOption "defaults for hosts"; 124 - options.user = defaultOption "defaults for users"; 125 - options.home = defaultOption "defaults for standalone homes"; 126 - }; 127 - }; 128 - }
nix/config.nix modules/config.nix
nix/default_packages.nix nix/template-packages.nix
+2 -17
nix/flakeModule.nix
··· 1 + { inputs, ... }: 1 2 { 2 - inputs, 3 - lib, 4 - config, 5 - ... 6 - }: 7 - let 8 - types = import ./types.nix { inherit inputs lib config; }; 9 - in 10 - { 11 - imports = [ 12 - ./scope.nix 13 - ./config.nix 14 - ./aspects.nix 15 - ]; 16 - options.den.hosts = types.hostsOption; 17 - options.den.homes = types.homesOption; 18 - config._module.args.den = config.den; 3 + imports = [ (inputs.import-tree ../modules) ]; 19 4 }
nix/scope.nix modules/scope.nix
nix/types.nix modules/_types.nix
+12 -12
templates/default/flake.lock
··· 22 22 }, 23 23 "den": { 24 24 "locked": { 25 - "lastModified": 1761614186, 26 - "narHash": "sha256-ZtwPLLfOMJa/RCimJsG7vKatVQ+iiVGsT6S/kXXOjK4=", 25 + "lastModified": 1761617317, 26 + "narHash": "sha256-QzXeoJ8YHTx866AjW4ocJXIaR7gvPolqba5I8dnKddw=", 27 27 "owner": "vic", 28 28 "repo": "den", 29 - "rev": "d74087a045d33efae12dfc665feca2904abee738", 29 + "rev": "fb490adb547f2f89539ee1cefad37f80aa2c2e1e", 30 30 "type": "github" 31 31 }, 32 32 "original": { ··· 37 37 }, 38 38 "flake-aspects": { 39 39 "locked": { 40 - "lastModified": 1761612377, 41 - "narHash": "sha256-0TW8xGpORlWt1mLjKqslAN3C/OM/EFnurCQjsSSQipQ=", 40 + "lastModified": 1761689934, 41 + "narHash": "sha256-gPib0lEup5yJP0LTeNiSCVymLbQceGRpX5kz2Z83/Zg=", 42 42 "owner": "vic", 43 43 "repo": "flake-aspects", 44 - "rev": "b94d806d772c9b4a78dd3f446228a099ac8f997b", 44 + "rev": "20ea52a231c688ef46339d2f4319d8ac0186a8a3", 45 45 "type": "github" 46 46 }, 47 47 "original": { ··· 92 92 ] 93 93 }, 94 94 "locked": { 95 - "lastModified": 1761584077, 96 - "narHash": "sha256-dISPEZahlfs5K6d58zR4akRRyogfE9P4WSyPPNT7HiE=", 95 + "lastModified": 1761666354, 96 + "narHash": "sha256-fHr+tIYBJccNF8QWqgowfRmEAtAMSt1deZIRNKL8A7c=", 97 97 "owner": "nix-community", 98 98 "repo": "home-manager", 99 - "rev": "e82585308aef3d4cc2c36c7b6946051c8cdf24ef", 99 + "rev": "ca2ab1d877a24d5a437dad62f56b8b2c02e964e9", 100 100 "type": "github" 101 101 }, 102 102 "original": { ··· 163 163 }, 164 164 "nixpkgs": { 165 165 "locked": { 166 - "lastModified": 1761440988, 167 - "narHash": "sha256-2qsow3cQIgZB2g8Cy8cW+L9eXDHP6a1PsvOschk5y+E=", 166 + "lastModified": 1761594641, 167 + "narHash": "sha256-sImk6SJQASDLQo8l+0zWWaBgg7TueLS6lTvdH5pBZpo=", 168 168 "owner": "nixos", 169 169 "repo": "nixpkgs", 170 - "rev": "de69d2ba6c70e747320df9c096523b623d3a4c35", 170 + "rev": "1666250dbe4141e4ca8aaf89b40a3a51c2e36144", 171 171 "type": "github" 172 172 }, 173 173 "original": {
+6
templates/default/modules/_example/_compat/hosts/honeycrisp/_darwin/something.nix
··· 1 + # this is a non-dendritic darwin class module file. 2 + # automatically discovered by `den.import-tree` as enabled in auto-imports.nix 3 + { ... }: 4 + { 5 + # see nix-darwin options. 6 + }
+7
templates/default/modules/_example/_compat/hosts/rockhopper/_nixos/hardware-auto-generated.nix
··· 1 + # this is a non-dendritic nix class module file. 2 + # automatically discovered by `den.import-tree` as enabled in auto-imports.nix 3 + # 4 + # suppose this file was auto-generated by nixos-generate-config or some other hardware tooling. 5 + { 6 + 7 + }
+86 -78
templates/default/modules/_example/aspects.nix
··· 1 1 # example aspect dependencies for our hosts 2 2 # Feel free to remove it, adapt or split into modules. 3 - { inputs, lib, ... }: 3 + # see also: defaults.nix, compat-imports.nix, home-managed.nix 4 + { 5 + inputs, 6 + den, 7 + lib, 8 + ... 9 + }: 4 10 { 11 + # see also defaults.nix where static settings are set. 12 + den.default = { 13 + # parametric defaults for host/user/home. see aspects/dependencies.nix 14 + # `_` is shorthand alias for `provides`. 15 + host.includes = [ den.aspects.example._.host ]; 16 + user.includes = [ den.aspects.example._.user ]; 17 + home.includes = [ den.aspects.example._.home ]; 18 + }; 5 19 6 - den.aspects = 7 - { aspects, ... }: 8 - { 9 - # rockhopper.nixos = { }; # config for rockhopper host 10 - # alice.homeManager = { }; # config for alice 11 - developer = { 12 - description = "aspect for bob's standalone home-manager"; 13 - homeManager = { }; 14 - }; 20 + # aspects for our example host/user/home definitions. 21 + # on a real setup you will split these over into multiple dendritic files. 22 + den.aspects = { 23 + rockhopper.nixos = { }; # config for rockhopper host 24 + # alice.homeManager = { }; # config for alice 15 25 16 - # aspect for adelie host using github:nix-community/NixOS-WSL 17 - wsl.nixos = { 18 - imports = [ inputs.nixos-wsl.nixosModules.default ]; 19 - wsl.enable = true; 20 - }; 21 - 22 - # default.{host,user,home} can be used for global settings. 23 - default.host.darwin.system.stateVersion = lib.mkDefault 6; 24 - default.host.nixos.system.stateVersion = "25.11"; 25 - default.home.homeManager.home.stateVersion = lib.mkDefault "25.11"; 26 + developer = { 27 + description = "aspect for bob's standalone home-manager"; 28 + homeManager = { }; 29 + }; 26 30 27 - # parametric host and user default configs. see aspects-config.nix 28 - default.host.includes = [ aspects.example.provides.host ]; 29 - default.user.includes = [ aspects.example.provides.user ]; 30 - default.home.includes = [ aspects.example.provides.home ]; 31 + # aspect for adelie host using github:nix-community/NixOS-WSL 32 + wsl.nixos = { 33 + imports = [ inputs.nixos-wsl.nixosModules.default ]; 34 + wsl.enable = true; 35 + }; 31 36 32 - # aspect for each host that includes the user alice. 33 - alice.provides.hostUser = 34 - { user, ... }: 35 - { 36 - # administrator in all nixos hosts 37 - nixos.users.users.${user.userName} = { 38 - isNormalUser = true; 39 - extraGroups = [ "wheel" ]; 40 - }; 37 + # aspect for each host that includes the user alice. 38 + alice.provides.hostUser = 39 + { user, ... }: 40 + { 41 + # administrator in all nixos hosts 42 + nixos.users.users.${user.userName} = { 43 + isNormalUser = true; 44 + extraGroups = [ "wheel" ]; 41 45 }; 42 - 43 - # subtree of aspects for demo purposes. 44 - example.provides = { 46 + }; 45 47 46 - # in our example, we allow all nixos hosts to be vm-bootable. 47 - vm-bootable = { 48 - nixos = 49 - { modulesPath, ... }: 50 - { 51 - imports = [ (modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix") ]; 52 - }; 53 - }; 48 + # subtree of aspects for demo purposes. 49 + example.provides = { 54 50 55 - # parametric providers. 56 - host = 57 - { host }: 58 - { class, ... }: 51 + # in our example, we allow all nixos hosts to be vm-bootable. 52 + vm-bootable = { 53 + nixos = 54 + { modulesPath, ... }: 59 55 { 60 - includes = [ aspects.example.provides.vm-bootable ]; 61 - ${class}.networking.hostName = host.hostName; 56 + imports = [ (modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix") ]; 62 57 }; 58 + }; 63 59 64 - user = 65 - { user, host }: 66 - let 67 - aspect = { 68 - name = "(example.user ${host.name} ${user.name})"; 69 - description = "user setup on different OS"; 70 - darwin.system.primaryUser = user.userName; 71 - nixos.users.users.${user.userName}.isNormalUser = true; 72 - }; 73 - 74 - # adelie is nixos-on-wsl, has special user setup 75 - by-host.adelie = { 76 - nixos.defaultUser = user.userName; 77 - }; 78 - in 79 - by-host.${host.name} or aspect; 60 + # parametric providers. 61 + host = 62 + { host }: 63 + { class, ... }: 64 + { 65 + # `_` is a shorthand alias for `provides` 66 + includes = [ den.aspects.example._.vm-bootable ]; 67 + ${class}.networking.hostName = host.hostName; 68 + }; 80 69 81 - home = 82 - { home }: 83 - { class, ... }: 84 - let 85 - path = if lib.hasSuffix "darwin" home.system then "/Users" else "/home"; 86 - in 87 - { 88 - ${class}.home = { 89 - username = home.userName; 90 - homeDirectory = "${path}/${home.userName}"; 91 - }; 70 + user = 71 + { user, host }: 72 + let 73 + by-class.nixos.users.users.${user.userName}.isNormalUser = true; 74 + by-class.darwin = { 75 + system.primaryUser = user.userName; 76 + users.users.${user.userName}.isNormalUser = true; 92 77 }; 93 78 94 - }; 79 + # adelie is nixos-on-wsl, has special additional user setup 80 + by-host.adelie.nixos.defaultUser = user.userName; 81 + in 82 + { 83 + includes = [ 84 + by-class 85 + (by-host.${host.name} or { }) 86 + ]; 87 + }; 88 + 89 + home = 90 + { home }: 91 + { class, ... }: 92 + let 93 + homeDir = if lib.hasSuffix "darwin" home.system then "/Users" else "/home"; 94 + in 95 + { 96 + ${class}.home = { 97 + username = lib.mkDefault home.userName; 98 + homeDirectory = lib.mkDefault "${homeDir}/${home.userName}"; 99 + }; 100 + }; 95 101 96 102 }; 103 + 104 + }; 97 105 }
+14
templates/default/modules/_example/compat-imports.nix
··· 1 + # configures class-automatic module auto imports for hosts/users/homes. 2 + # See _example/hosts/*/_${class}/*.nix 3 + { den, ... }: 4 + { 5 + 6 + # alice imports non-dendritic <class> modules from _compat/alice/_<class>/*.nix 7 + den.aspects.alice.includes = [ (den.import-tree ./_compat/alice) ]; 8 + 9 + # See the documentation at batteries/import-tree.nix 10 + den.default.host.includes = [ (den.import-tree._.host ./_compat/hosts) ]; 11 + den.default.user.includes = [ (den.import-tree._.user ./_compat/users) ]; 12 + den.default.home.includes = [ (den.import-tree._.home ./_compat/homes) ]; 13 + 14 + }
+8
templates/default/modules/_example/defaults.nix
··· 1 + { 2 + # default.{host,user,home} aspects can be used for global settings. 3 + den.default = { 4 + host.darwin.system.stateVersion = 6; 5 + host.nixos.system.stateVersion = "25.11"; 6 + home.homeManager.home.stateVersion = "25.11"; 7 + }; 8 + }
+5
templates/default/modules/_example/home-managed.nix
··· 1 + { den, ... }: 2 + { 3 + # see batteries/home-manager.nix 4 + den.default.host.includes = [ den.home-manager ]; 5 + }