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

Home integration now enabled via host config for hm, nix-maid, hjem (#215)

This deprecates the `den.provides.home-manager` aspect.

authored by oeiuwq.com and committed by

GitHub e67df226 9a36e899

+209 -94
+2 -2
docs/astro.config.mjs
··· 62 62 items: [ 63 63 { label: 'Declare Hosts & Users', slug: 'guides/declare-hosts' }, 64 64 { label: 'Configure Aspects', slug: 'guides/configure-aspects' }, 65 - { label: 'Home-Manager Integration', slug: 'guides/home-manager' }, 65 + { label: 'Custom Nix Classes', slug: 'guides/custom-classes' }, 66 + { label: 'Homes Integration', slug: 'guides/home-manager' }, 66 67 { label: 'Use Batteries', slug: 'guides/batteries' }, 67 68 { label: 'Share with Namespaces', slug: 'guides/namespaces' }, 68 69 { label: 'Angle Brackets Syntax', slug: 'guides/angle-brackets' }, 69 - { label: 'Custom Nix Classes', slug: 'guides/custom-classes' }, 70 70 { label: 'Migrate to Den', slug: 'guides/migrate' }, 71 71 { label: 'Debug Configurations', slug: 'guides/debug' }, 72 72 ],
+29
docs/src/content/docs/guides/custom-classes.mdx
··· 45 45 } 46 46 ``` 47 47 48 + Example: A git class that forwards to home-manager. 49 + 50 + ```nix 51 + gitClass = 52 + { class, aspect-chain }: 53 + den._.forward { 54 + each = lib.singleton true; 55 + fromClass = _: "git"; 56 + intoClass = _: "homeManager"; 57 + intoPath = _: [ 58 + "programs" 59 + "git" 60 + ]; 61 + fromAspect = _: lib.head aspect-chain; 62 + adaptArgs = 63 + { config, ... }: 64 + { 65 + osConfig = config; 66 + }; 67 + }; 68 + 69 + den.aspects.tux = { 70 + includes = [ gitClass ]; 71 + git.userEmail = "root@linux.com"; 72 + }; 73 + ``` 74 + 75 + This will set at host: `igloo.home-manager.users.tux.programs.git.userEmail` 76 + 48 77 49 78 ## Use Cases 50 79
+58 -49
docs/src/content/docs/guides/home-manager.mdx
··· 1 1 --- 2 - title: Home-Manager Integration 2 + title: Homes Integration (hm / hjem / nix-maid, etc) 3 3 description: Integrate Home-Manager into NixOS/Darwin hosts or use standalone. 4 4 --- 5 5 ··· 8 8 9 9 <Aside title="Use the Source, Luke" icon="github"> 10 10 [`hm-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-os.nix) · [`hm-integration.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-integration.nix) 11 + 12 + [`hjem-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/hjem/hjem-os.nix) · [`hjem-integration.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/hjem/hjem-integration.nix) 13 + 14 + [`maid-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/maid/maid-os.nix) · [`maid-integration.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/maid/maid-integration.nix) 15 + 11 16 </Aside> 12 17 13 - ## Automatic HM Detection 18 + Den supports different Nix libraries for managing user home files. 19 + All of them have Nix classes implemented using `den.provides.forward` 20 + and you can create custom home-like classes following the same pattern. 14 21 15 - Den automatically detects hosts that need Home-Manager. When a host has 16 - users with `"homeManager"` in their `classes` list (the default), Den: 22 + - `user` class. NixOS and nix-Darwin builtin `users.users.<userName>` submodule. 23 + - `homeManager` class. Enabled via `host.home-manager.enable` 24 + - `hjem` class. Enabled via `host.hjem.enable` 25 + - `maid` class. Enabled via `host.nix-maid.enable`. 17 26 18 - 1. Imports the Home-Manager NixOS/Darwin module 19 - 2. Creates a `homeManager` class for each user 20 - 3. Forwards `homeManager` configs into `home-manager.users.<name>` 27 + Class names follow the Nix class used by the module system of these projects 28 + not the project name, e.g: [`homeManager`](https://github.com/nix-community/home-manager/blob/f140aa04d7d14f8a50ab27f3691b5766b17ae961/modules/default.nix#L32). 21 29 22 - No explicit opt-in needed — just declare users. 30 + Except for the `user` class which is always enabled, other home classes 31 + are opt-in features, enabled via the host configuration: 23 32 24 - ```mermaid 25 - graph LR 26 - Host["{host}"] -->|"hm-detect"| Check{"class ∈ nixos,darwin?<br/>HM users exist?<br/>HM module available?"} 27 - Check -->|"yes"| HM["ctx.hm-host<br/>imports HM module"] 28 - HM -->|"per user"| FW["forward homeManager<br/>→ home-manager.users.‹name›"] 29 - Check -->|"no"| Skip["HM pipeline skipped"] 33 + ```nix 34 + # per host: 35 + den.hosts.x86_64-linux.igloo.home-manager = { 36 + enable = true; 37 + 38 + # optionally with custom stable/unstable HM input 39 + module = inputs.home-manager-unstable.nixosModules.default; 40 + }; 41 + 42 + # Globally (or conditionally on host) 43 + den.base.host = { host, ... }: { 44 + home-manager.enable = host.name == "igloo"; 45 + }; 30 46 ``` 31 47 48 + ## Multi Home support. 49 + 50 + User can choose to manage some files with home-manager instead or in 51 + addition to nix-maid and hjem. For example, when migrating away from home-manager into other libraries, you might want to keep your stuff working as you move progressively. 52 + 53 + Users define a `<user>.classes = [ "homeManager" ]` (default) where they 54 + explicitly enable the different home libraries they use. 55 + 32 56 ## Host-Managed Users 33 57 34 58 ```nix 35 59 den.hosts.x86_64-linux.igloo.users.tux = { }; 36 - den.default.homeManager.home.stateVersion = "25.11"; 60 + 61 + # host meta configuration (capabilities), HM always enabled. 62 + den.base.host.home-manager.enable = lib.mkDefault true; 63 + 64 + # homeManager default settings 65 + den.default.homeManager.home.stateVersion = lib.mkDefault "25.11"; 37 66 67 + # NixOS/Nix-Darwin user class. 68 + den.aspects.tux.user.description = "The Penguin"; 69 + 70 + # homeManager class 38 71 den.aspects.tux.homeManager.programs.vim.enable = true; 39 72 ``` 40 73 74 + User description forwarded into `igloo.users.users.tux.description`. 41 75 The vim config is forwarded into `igloo.home-manager.users.tux`. 42 76 43 77 ## Standalone Home-Manager ··· 52 86 53 87 Build with `home-manager switch --flake .#vic`. 54 88 55 - ## The define-user Battery 89 + ## Context types for Home enabled hosts. 56 90 57 - Use `den._.define-user` to automatically set username and home directory: 91 + Each Home integration in Den defines a context 92 + transformation from `den.ctx.host -> den.ctx.${name}-host`. 58 93 59 - ```nix 60 - den.default.includes = [ den._.define-user ]; 61 - ``` 94 + For example, `den.ctx.hm-host` is the aspect for Home-Manager enabled hosts. 95 + Similarly `den.ctx.maid-host` and `den.ctx.hjem-host`. 62 96 63 - This sets `home.username`, `home.homeDirectory`, and 64 - `users.users.<name>` on both NixOS and Darwin. 97 + ## useGlobalPkgs at Home Manager enabled hosts 65 98 66 - ## useGlobalPkgs 67 - 68 - Configure Home-Manager to use the host's nixpkgs: 99 + Configure a NixOS option only for Home-Manager hosts 100 + so they can use the host's nixpkgs: 69 101 70 102 ```nix 71 103 den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true; 72 104 ``` 73 105 74 106 This only activates for hosts that actually have Home-Manager users. 75 - Hosts without users are unaffected. 76 - 77 - ## Custom HM Module 78 - 79 - Override the Home-Manager module source: 80 - 81 - ```nix 82 - den.hosts.x86_64-linux.igloo = { 83 - hm-module = inputs.home-manager-unstable.nixosModules.home-manager; 84 - users.tux = { }; 85 - }; 86 - ``` 107 + Hosts without Home-Manager users are unaffected. 87 108 88 109 ## Standalone with osConfig 89 110 ··· 104 125 }; 105 126 ``` 106 127 107 - ## Context Hooks 108 128 109 - Use `den.ctx.hm-host` to configure things only when HM is active: 110 - 111 - ```nix 112 - den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true; 113 - den.ctx.hm-host.includes = [ 114 - { nixos.home-manager.backupFileExtension = "bak"; } 115 - ]; 116 - ``` 117 - 118 - These only apply to hosts that have Home-Manager users with 119 - a supported OS (NixOS or Darwin).
+1 -1
docs/src/content/docs/overview.mdx
··· 1 1 --- 2 - title: Overview 2 + title: Documentation Overview 3 3 description: Everything Den 4 4 --- 5 5
+21 -8
modules/aspects/provides/hjem/hjem-os.nix
··· 10 10 "nixos" 11 11 "darwin" 12 12 ]; 13 - hjemModule = host: host.hjem-module or inputs.hjem."${host.class}Modules".default; 13 + 14 + hostConf = 15 + { host, ... }: 16 + { 17 + options.hjem = { 18 + enable = lib.mkEnableOption "Enable hjem"; 19 + module = lib.mkOption { 20 + type = lib.types.deferredModule; 21 + default = inputs.hjem."${host.class}Modules".default; 22 + }; 23 + }; 24 + }; 14 25 15 26 hjemDetect = 16 27 { host }: 17 28 let 18 29 isOsSupported = builtins.elem host.class hjemOsClasses; 19 - hasHjemModule = (host ? hjem-module) || (inputs ? hjem); 20 30 hjemUsers = builtins.filter (u: lib.elem hjemClass u.classes) (lib.attrValues host.users); 21 31 hasHjemUsers = builtins.length hjemUsers > 0; 22 - isHjemHost = isOsSupported && hasHjemUsers && hasHjemModule; 32 + isHjemHost = host.hjem.enable && isOsSupported && hasHjemUsers; 23 33 in 24 34 lib.optional isHjemHost { inherit host; }; 25 35 26 - in 27 - { 28 - den.ctx.host.into.hjem-host = hjemDetect; 36 + ctx.host.into.hjem-host = hjemDetect; 29 37 30 - den.ctx.hjem-host._.hjem-host = 38 + ctx.hjem-host._.hjem-host = 31 39 { host }: 32 40 { 33 - ${host.class}.imports = [ (hjemModule host) ]; 41 + ${host.class}.imports = [ host.hjem.module ]; 34 42 }; 43 + 44 + in 45 + { 46 + den.ctx = ctx; 47 + den.base.host.imports = [ hostConf ]; 35 48 }
+23 -1
modules/aspects/provides/home-manager/hm-integration.nix
··· 43 43 44 44 in 45 45 { 46 - den.provides.home-manager = { }; 46 + den.provides.home-manager = 47 + _: 48 + throw '' 49 + NOTICE: den.provides.home-manager aspect is not needed anymore. 50 + 51 + Enabling Home Manager is now made via the host config: 52 + 53 + Per host: 54 + 55 + den.hosts.x86_64-linux.igloo.home-manager.enable = true; 56 + 57 + On ALL hosts: 58 + 59 + den.base.host.home-manager.enable = true; 60 + 61 + See <den/home-manager/hm-os.nix> 62 + 63 + If you had includes at den._.home-manager, you can use: 64 + 65 + den.ctx.hm-host.includes = [ ... ]; 66 + 67 + For attaching aspects to home-manager enabled hosts. 68 + ''; 47 69 48 70 den.ctx.home.description = "Standalone Home-Manager config provided by home aspect"; 49 71 den.ctx.home._.home = { home }: parametric.fixedTo { inherit home; } den.aspects.${home.aspect};
+25 -12
modules/aspects/provides/home-manager/hm-os.nix
··· 9 9 Detects hosts that have an HM supported OS and 10 10 that have at least one user with ${hm-class} class. 11 11 12 - When this occurs it produces a context `den.ctx.hm-os` 12 + When this occurs it produces a context `den.ctx.hm-host` 13 13 14 14 This `den.ctx.hm-os` context includes the OS-level 15 15 homeManager module and is used by hm-integration.nix to then 16 - produce a `den.ctx.hm` for each user. 16 + produce a `den.ctx.hm-user` for each user. 17 17 18 18 This same context can be used to include aspects 19 19 ONLY for hosts having HM enabled. 20 20 21 - den.ctx.hm-os.includes = [ den.aspects.foo ]; 21 + den.ctx.hm-host.includes = [ den.aspects.foo ]; 22 22 ''; 23 23 24 24 hm-class = "homeManager"; ··· 26 26 "nixos" 27 27 "darwin" 28 28 ]; 29 - hm-module = host: host.hm-module or inputs.home-manager."${host.class}Modules".home-manager; 29 + 30 + hostConf = 31 + { host, ... }: 32 + { 33 + options.home-manager = { 34 + enable = lib.mkEnableOption "Enable Home Manager integration"; 35 + module = lib.mkOption { 36 + type = lib.types.deferredModule; 37 + default = inputs.home-manager."${host.class}Modules".home-manager; 38 + }; 39 + }; 40 + }; 30 41 31 42 hm-detect = 32 43 { host }: 33 44 let 34 45 is-os-supported = builtins.elem host.class hm-os-classes; 35 - has-hm-module = (host ? hm-module) || (inputs ? home-manager); 36 46 hm-users = builtins.filter (u: lib.elem hm-class u.classes) (lib.attrValues host.users); 37 47 has-hm-users = builtins.length hm-users > 0; 38 - is-hm-host = is-os-supported && has-hm-users && has-hm-module; 48 + is-hm-host = host.home-manager.enable && is-os-supported && has-hm-users; 39 49 in 40 50 lib.optional is-hm-host { inherit host; }; 41 51 42 - in 43 - { 44 - den.ctx.host.into.hm-host = hm-detect; 52 + ctx.host.into.hm-host = hm-detect; 45 53 46 - den.ctx.hm-host.description = description; 47 - den.ctx.hm-host._.hm-host = 54 + ctx.hm-host.description = description; 55 + ctx.hm-host.provides.hm-host = 48 56 { host }: 49 57 { 50 - ${host.class}.imports = [ (hm-module host) ]; 58 + ${host.class}.imports = [ host.home-manager.module ]; 51 59 }; 60 + 61 + in 62 + { 63 + den.ctx = ctx; 64 + den.base.host.imports = [ hostConf ]; 52 65 }
+20 -8
modules/aspects/provides/maid/maid-os.nix
··· 9 9 maidOsClasses = [ 10 10 "nixos" 11 11 ]; 12 - maidModule = host: host.maid-module or inputs.nix-maid.nixosModules.default; 12 + 13 + hostConf = 14 + { host, ... }: 15 + { 16 + options.nix-maid = { 17 + enable = lib.mkEnableOption "Enable nix-maid"; 18 + module = lib.mkOption { 19 + type = lib.types.deferredModule; 20 + default = inputs.nix-maid.nixosModules.default; 21 + }; 22 + }; 23 + }; 13 24 14 25 maidDetect = 15 26 { host }: 16 27 let 17 28 isOsSupported = builtins.elem host.class maidOsClasses; 18 - hasMaidModule = (host ? maid-module) || (inputs ? nix-maid); 19 29 maidUsers = builtins.filter (u: lib.elem maidClass u.classes) (lib.attrValues host.users); 20 30 hasMaidUsers = builtins.length maidUsers > 0; 21 - isMaidHost = isOsSupported && hasMaidUsers && hasMaidModule; 31 + isMaidHost = host.nix-maid.enable && isOsSupported && hasMaidUsers; 22 32 in 23 33 lib.optional isMaidHost { inherit host; }; 24 34 25 - in 26 - { 27 - den.ctx.host.into.maid-host = maidDetect; 35 + ctx.host.into.maid-host = maidDetect; 28 36 29 - den.ctx.maid-host._.maid-host = 37 + ctx.maid-host._.maid-host = 30 38 { host }: 31 39 { 32 - ${host.class}.imports = [ (maidModule host) ]; 40 + ${host.class}.imports = [ host.nix-maid.module ]; 33 41 }; 42 + in 43 + { 44 + den.ctx = ctx; 45 + den.base.host.imports = [ hostConf ]; 34 46 }
+1
templates/bogus/modules/test-base.nix
··· 28 28 29 29 denModule = { 30 30 imports = [ inputs.den.flakeModule ]; 31 + den.base.host.home-manager.enable = true; 31 32 den.default.homeManager.home.stateVersion = "26.05"; 32 33 den.default.nixos = { 33 34 system.stateVersion = "26.05";
+8 -5
templates/ci/modules/features/hjem-class.nix
··· 25 25 { 26 26 den.hosts.x86_64-linux.igloo = { 27 27 users.tux.classes = [ "hjem" ]; 28 - hjem-module = mockHjemModule; 28 + hjem.enable = true; 29 + hjem.module = mockHjemModule; 29 30 }; 30 31 31 32 den.aspects.tux.hjem.theme = "nord"; ··· 45 46 { 46 47 den.hosts.x86_64-linux.igloo = { 47 48 users.tux.classes = [ "hjem" ]; 48 - hjem-module = mockHjemModule; 49 + hjem.enable = true; 50 + hjem.module = mockHjemModule; 49 51 }; 50 52 51 53 den.aspects.tux.hjem.tags = [ "from-hjem" ]; ··· 64 66 { 65 67 den.hosts.x86_64-linux.igloo = { 66 68 users.tux = { }; 67 - hjem-module = mockHjemModule; 69 + hjem.enable = false; 70 + hjem.module = mockHjemModule; 68 71 }; 69 72 70 - expr = igloo.networking.hostName; 71 - expected = "nixos"; 73 + expr = igloo ? hjem; 74 + expected = false; 72 75 } 73 76 ); 74 77
+8 -5
templates/ci/modules/features/maid-class.nix
··· 29 29 { 30 30 den.hosts.x86_64-linux.igloo = { 31 31 users.tux.classes = [ "maid" ]; 32 - maid-module = mockMaidModule; 32 + nix-maid.enable = true; 33 + nix-maid.module = mockMaidModule; 33 34 }; 34 35 35 36 den.aspects.tux.maid.description = "maid-tux"; ··· 49 50 { 50 51 den.hosts.x86_64-linux.igloo = { 51 52 users.tux.classes = [ "maid" ]; 52 - maid-module = mockMaidModule; 53 + nix-maid.enable = true; 54 + nix-maid.module = mockMaidModule; 53 55 }; 54 56 55 57 den.aspects.tux.maid.tags = [ "from-maid" ]; ··· 68 70 { 69 71 den.hosts.x86_64-linux.igloo = { 70 72 users.tux = { }; 71 - maid-module = mockMaidModule; 73 + nix-maid.enable = false; 74 + nix-maid.module = mockMaidModule; 72 75 }; 73 76 74 - expr = igloo.networking.hostName; 75 - expected = "nixos"; 77 + expr = igloo ? users.tux.maid; 78 + expected = false; 76 79 } 77 80 ); 78 81
+2
templates/ci/modules/test-support/eval-den.nix
··· 31 31 options.flake.packages = lib.mkOption { }; 32 32 options.expr = lib.mkOption { }; 33 33 options.expected = lib.mkOption { }; 34 + 35 + config.den.base.host.home-manager.enable = lib.mkDefault true; 34 36 }; 35 37 36 38 helpersModule =
+4 -1
templates/default/modules/hosts.nix
··· 2 2 # then config their aspects in as many files you want 3 3 { 4 4 # tux user at igloo host. 5 - den.hosts.x86_64-linux.igloo.users.tux = { }; 5 + den.hosts.x86_64-linux.igloo = { 6 + users.tux = { }; 7 + home-manager.enable = true; 8 + }; 6 9 7 10 # define an standalone home-manager for tux 8 11 # den.homes.x86_64-linux.tux = { };
+3
templates/example/modules/den.nix
··· 2 2 den.hosts.x86_64-linux.igloo.users.alice = { }; 3 3 den.hosts.aarch64-darwin.apple.users.alice = { }; 4 4 den.homes.x86_64-linux.alice = { }; 5 + 6 + # Enable HM on all hosts 7 + den.base.host.home-manager.enable = true; 5 8 }
+1
templates/minimal/modules/den.nix
··· 19 19 20 20 den.aspects.tux = { 21 21 includes = [ den.provides.primary-user ]; 22 + user.extraGroups = [ "audio" ]; 22 23 }; 23 24 }
+3 -2
templates/noflake/modules/den.nix
··· 10 10 }; 11 11 12 12 # tux user on igloo host, using nix-maid 13 - den.hosts.x86_64-linux.igloo.users.tux = { 14 - classes = [ "maid" ]; 13 + den.hosts.x86_64-linux.igloo = { 14 + nix-maid.enable = true; 15 + users.tux.classes = [ "maid" ]; 15 16 }; 16 17 17 18 # host aspect