···2223- [Dendritic](https://github.com/mightyiam/dendritic): **same** concern across **different** Nix classes.
2425-- Create [DRY](modules/aspects/provides/unfree.nix) & [`class`-generic](modules/aspects/provides/primary-user.nix) modules.
002627- [Parametric](modules/aspects/provides/define-user.nix) over `host`/`home`/`user`.
2829-- Context-aware [dependencies](modules/aspects/dependencies.nix): user/host contributions.
3031-- [templates/noflake](templates/noflake). Works with stable Nix (no-flakes) and needs no flake-parts.
32-33-- [Share](templates/default/modules/namespace.nix) aspects across systems & repos.
3435-- [Routable](templates/default/modules/aspects/eg/routes.nix) configurations.
3637-- Custom factories for any Nix `class`.
3839- Use `stable`/`unstable` channels per config.
4041-- Freeform `host`/`user`/`home` [schemas](modules/_types.nix) (no `specialArgs`) with base modules.
4243- Multi-platform, multi-tenant hosts.
44···4647- Opt-in [`<angle/brackets>`](https://vic.github.io/den/angle-brackets.html) aspect resolution.
4849-- Templates [tested](templates/default/modules/tests.nix) along [examples](templates/examples/modules/_example/ci).
5051-- Concepts [documented](https://vic.github.io/den).
5253Need more **batteries**? See [vic/denful](https://github.com/vic/denful).
54···77**Available templates**
7879- [`default`](templates/default) batteries-included layout.
80-- [`minimal`](templates/minimal) truly minimalistic start.
81-- [`examples`](templates/examples) tests for all features.
0082- [`bogus`](templates/bogus) reproduce and report bugs.
8384</div>
85</td>
86<td>
8788-๐ Define [Hosts, Users](templates/examples/modules/_example/hosts.nix) & [Homes](templates/examples/modules/_example/homes.nix) concisely.
0000000000000000000000000000000008990-See schema in [`_types.nix`](modules/_types.nix).
0000000000000000000000009192```nix
93# modules/hosts.nix
94{
95- # same home-manager vic configuration
96- # over laptop, macbook and standalone-hm
97 den.hosts.x86_64-linux.lap.users.vic = {};
98 den.hosts.aarch64-darwin.mac.users.vic = {};
99 den.homes.aarch64-darwin.vic = {};
···106$ home-manager switch --flake .#vic
107```
108109-๐งฉ [Aspect-oriented](https://github.com/vic/flake-aspects) incremental features. ([example](templates/default/modules/den.nix))
110-111-Any module can contribute configurations to aspects.
112113```nix
114# modules/my-laptop.nix
···154155 };
156}
01570158# modules/vic.nix
159{ den, ... }: {
160 den.aspects.vic = {
···165 };
166 includes = [
167 den.aspects.tiling-wm
168- den._.primary-user
169 ];
170 };
171}
···177178You are done! You know everything to start creating configurations with `den`.
179180-Feel free to to **explore** the codebase, particularly our [included batteries](modules/aspects/provides) and [tests](templates/examples/modules/_example/ci).
181182## Learn more at our [documentation website](https://vic.github.io/den)
183
···2223- [Dendritic](https://github.com/mightyiam/dendritic): **same** concern across **different** Nix classes.
2425+- [Flake optional](templates/noflake). Works with _stable_/_unstable_ Nix and with/without flake-parts.
26+27+- Create [DRY](modules/aspects/provides/unfree/unfree.nix) & [`class`-generic](modules/aspects/provides/primary-user.nix) modules.
2829- [Parametric](modules/aspects/provides/define-user.nix) over `host`/`home`/`user`.
3031+- Context-aware [dependencies](modules/aspects/dependencies.nix): user\<->host bidirectional contributions.
3233+- [Share](templates/example/modules/namespace.nix) aspects across systems & repos.
003435+- [Routable](templates/example/modules/aspects/eg/routes.nix) configurations.
3637+- Custom [factories](https://github.com/vic/den/blob/f5c44098e4855e07bf5cbcec00509e75ddde4220/templates/ci/modules/homes.nix#L20) for any Nix `class`.
3839- Use `stable`/`unstable` channels per config.
4041+- Freeform `host`/`user`/`home` [schemas](modules/_types.nix) (no `specialArgs`) with [base](https://github.com/vic/den/pull/119) modules.
4243- Multi-platform, multi-tenant hosts.
44···4647- Opt-in [`<angle/brackets>`](https://vic.github.io/den/angle-brackets.html) aspect resolution.
4849+- Features [tested](templates/ci).
5051+- REPL [friendly](https://github.com/vic/den/blob/f5c44098e4855e07bf5cbcec00509e75ddde4220/templates/bogus/modules/bug.nix#L34) [debugging](https://den.oeiuwq.com/debugging.html).
5253Need more **batteries**? See [vic/denful](https://github.com/vic/denful).
54···77**Available templates**
7879- [`default`](templates/default) batteries-included layout.
80+- [`minimal`](templates/minimal) minimalistic flake.
81+- [`noflake`](templates/noflake) no flakes, no flake-parts, user nix-maid.
82+- [`example`](templates/example) examples.
83+- [`ci`](templates/ci) tests for all features.
84- [`bogus`](templates/bogus) reproduce and report bugs.
8586</div>
87</td>
88<td>
8990+### Den fundamental idea
91+92+> Configurations that can be applied to multiple host/user combinations.
93+> The [`__functor`](https://den.oeiuwq.com/functor.html) pattern makes aspects parametric.
94+95+<details>
96+97+<summary>
98+ Den is about propagating context to produce configs.
99+</summary>
100+101+```nix
102+# context => aspect
103+{ host, user, ... }@context: {
104+ # any Nix configuration classes
105+ nixos = { };
106+ darwin = { };
107+ homeManager = { };
108+ # supports conditional includes that inspect context
109+ # unlike nix-module imports
110+ includes = [ ];
111+}
112+```
113+114+You first define which [Hosts, Users](templates/ci/modules/hosts.nix)
115+and [Homes](templates/ci/modules/homes.nix) exist
116+using freeform-attributes or base-modules.
117+118+```nix
119+{
120+ # isWarm or any other freeform attr
121+ den.hosts.x86_64-linux.igloo.isWarm = true;
122+}
123+```
124125+Then, you write functions from context `host`/`user` to configs.
126+127+```nix
128+{
129+ den.aspects.heating = { host, user, ... }: {
130+ nixos = { ... }; # depends on host.isWarm, etc.
131+ homeManager = { ... };
132+ };
133+134+ # previous aspect can be included on any host
135+ # den.aspects.igloo.includes = [ den.aspects.heating ];
136+ # or by default in all of them
137+ # den.default.includes = [ den.aspects.heating ];
138+}
139+```
140+141+This way, configurations are truly re-usable,
142+as they are nothing more than functions of the
143+particularities of the host or its users.
144+145+</details>
146+147+### Code example
148+149+Schema based hosts/users/homes entities (see [`_types.nix`](modules/_types.nix)).
150151```nix
152# modules/hosts.nix
153{
154+ # same vic home-manager aspect shared
155+ # on laptop, macbook and standalone-hm
156 den.hosts.x86_64-linux.lap.users.vic = {};
157 den.hosts.aarch64-darwin.mac.users.vic = {};
158 den.homes.aarch64-darwin.vic = {};
···165$ home-manager switch --flake .#vic
166```
167168+Any module can contribute configurations to [aspects](https://github.com/vic/flake-aspects).
00169170```nix
171# modules/my-laptop.nix
···211212 };
213}
214+```
215216+```nix
217# modules/vic.nix
218{ den, ... }: {
219 den.aspects.vic = {
···224 };
225 includes = [
226 den.aspects.tiling-wm
227+ den.provides.primary-user
228 ];
229 };
230}
···236237You are done! You know everything to start creating configurations with `den`.
238239+Feel free to to **explore** the codebase, particularly our [included batteries](modules/aspects/provides) and [tests](templates/ci).
240241## Learn more at our [documentation website](https://vic.github.io/den)
242
···1+{ inputs, lib, ... }:
2+if inputs ? flake-parts then
3+ { }
4+else
5+ {
6+ # NOTE: Currently Den needs a top-level attribute where to place configurations,
7+ # by default it is the `flake` attribute, even if Den uses no flake-parts at all.
8+ options.flake = lib.mkOption {
9+ type = lib.types.submodule { freeformType = lib.types.anything; };
10+ };
11+ }
···45Create a **minimal** bug reproduction at [`modules/bug.nix`](modules/bug.nix)
6007Then run tests:
89-```console
10nix flake check
11```
1213-Format code with:
0000001415-```console
16-nix fmt
017```
···45Create a **minimal** bug reproduction at [`modules/bug.nix`](modules/bug.nix)
67+See also [Den debugging tips](https://den.oeiuwq.com/debugging.html)
8+9Then run tests:
1011+```shell
12nix flake check
13```
1415+Please share a link to your reproduction repo, showing the CI step and the
16+error at CI build.
17+18+## Fixing Den
19+20+If you are contributing a bug-fix PR, you can use the following command to
21+use your local den checkout.
2223+```shell
24+cd <den-working-copy>
25+nix flake check --override-input den . ./templates/bogus
26```
···1-# DO-NOT-CHANGE. Keep your reproduction minimalistic!
2-#
3-# try not adding new inputs
4-# but if you have no options (pun intended)
5-# here's the place.
6-#
7-# IF you make any change to this file, use:
8-# `nix run .#write-flake`
9-#
10-# We provide nix-darwin and home-manager for common usage.
11-{
12- # change "main" with a commit where bug is present
13- flake-file.inputs.den.url = "github:vic/den/main";
14-15- # included so we can test HM integrations.
16- flake-file.inputs.home-manager = {
17- url = "github:nix-community/home-manager";
18- inputs.nixpkgs.follows = "nixpkgs";
19- };
20-21- # included for testing darwin hosts.
22- flake-file.inputs.darwin = {
23- url = "github:nix-darwin/nix-darwin";
24- inputs.nixpkgs.follows = "nixpkgs";
25- };
26-}
···1+# tests for extending `den.base.*` modules with capabilities (options).
2+# these allow you to expend all hosts/users/homes with custom option
3+# that can later be used by aspects for providing features.
4+#
5+{ lib, ... }:
6+{
7+8+ # This module is base for all host configs.
9+ den.base.host =
10+ { host, ... }:
11+ {
12+ options.capabilities.ssh-server = lib.mkEnableOption "Does host ${host.name} provide ssh?";
13+ };
14+15+ # This module is base for all user configs.
16+ den.base.user =
17+ { user, ... }:
18+ {
19+ options.isAdmin = lib.mkOption {
20+ type = lib.types.bool;
21+ default = user.name == "alice"; # only alice is always admin
22+ };
23+ };
24+25+ # This module is base for all home configs.
26+ # den.base.home = { home, ... }: { };
27+28+ # This one is included on each host/user/home
29+ # it cannot access host/user/home values since this conf is generic.
30+ den.base.conf = {
31+ options.foo = lib.mkOption {
32+ type = lib.types.str;
33+ default = "bar";
34+ };
35+ };
36+37+ # Now hosts and users can set any option defined in base modules.
38+ den.hosts.x86_64-linux.rockhopper = {
39+ capabilities.ssh-server = true;
40+ foo = "boo";
41+ users.alice = {
42+ # isAdmin = true; # alice is admin by default, nothing explicit here.
43+ foo = "moo";
44+ };
45+ };
46+47+ den.aspects.rockhopper.includes =
48+ let
49+ # An aspect can make use of these options to provide configuration.
50+ sshCapable =
51+ { host, ... }:
52+ {
53+ nixos.services.sshd.enable = host.capabilities.ssh-server;
54+ homeManager.services.ssh-agent.enable = host.capabilities.ssh-server;
55+ };
56+ in
57+ [ sshCapable ];
58+59+ # CI checks
60+ perSystem =
61+ {
62+ checkCond,
63+ rockhopper,
64+ alice-at-rockhopper,
65+ ...
66+ }:
67+ {
68+ checks.host-conf-rockhopper-sshd = checkCond "sshd enabled" (
69+ rockhopper.config.services.sshd.enable == true
70+ );
71+ checks.host-conf-alice-sshd = checkCond "ssh-agent enabled" (
72+ alice-at-rockhopper.services.ssh-agent.enable == true
73+ );
74+ };
75+76+}
···1+# User TODO: Remove this file.
2+{
3+ # default aspect can be used for global static settings.
4+ den.default = {
5+ # static values.
6+ darwin.system.stateVersion = 6;
7+ nixos.system.stateVersion = "25.05";
8+ homeManager.home.stateVersion = "25.05";
9+10+ # these defaults are set for checking with CI.
11+ nixos.programs.vim.enable = true;
12+ darwin.programs.zsh.enable = true;
13+ };
14+}
+27
templates/ci/modules/define-user.nix
···000000000000000000000000000
···1+{ den, ... }:
2+{
3+ den.default.includes = [
4+ # Example: parametric over many contexts: { home }, { host, user }, { fromUser, toHost }
5+ den.provides.define-user
6+ ];
7+8+ perSystem =
9+ {
10+ checkCond,
11+ rockhopper,
12+ adelie,
13+ ...
14+ }:
15+ {
16+17+ checks.alice-exists-on-rockhopper = checkCond "den.default.user.includes defines user on host" (
18+ rockhopper.config.users.users.alice.isNormalUser
19+ );
20+ checks.alice-not-exists-on-adelie = checkCond "den.default.user.includes defines user on host" (
21+ !adelie.config.users.users ? alice
22+ );
23+ checks.will-exists-on-adelie = checkCond "den.default.user.includes defines user on host" (
24+ adelie.config.users.users.will.isNormalUser
25+ );
26+ };
27+}
···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+}
···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+# USER TODO: Remove this file.
5+# suppose this file was auto-generated by nixos-generate-config or some other hardware tooling.
6+{ lib, ... }:
7+{
8+ # used in CI to test this file was actually imported.
9+ options.auto-imported = lib.mkOption {
10+ readOnly = true;
11+ type = lib.types.bool;
12+ default = true;
13+ };
14+}
···16nix flake check
17```
1819-- Read [modules/den.nix](modules/den.nix) where hosts and homes definitions are for this example.
20-21-- Read [modules/namespace.nix](modules/namespace.nix) where a new `eg` (an example) aspects namespace is created.
22-23-- Read [modules/aspects/igloo.nix](modules/aspects/igloo.nix) where the `igloo` host is configured.
24-25-- Read [modules/aspects/alice.nix](modules/aspects/alice.nix) where the `alice` user is configured.
26-27-- Run the VM.
28-29-```console
30-nix run .#vm
31-```
32-33-- Edit and run VM loop.
34-35-Feel free to add more aspects, organize things to your liking.
···1-{ den, eg, ... }:
2-{
3- den.aspects.alice = {
4-5- # Alice can include other aspects.
6- # For small, private one-shot aspects, use let-bindings like here.
7- # for more complex or re-usable ones, define on their own modules,
8- # as part of any aspect-subtree.
9- includes =
10- let
11- # hack for nixf linter to keep findFile :/
12- unused = den.lib.take.unused __findFile;
13- __findFile = unused den.lib.__findFile;
14-15- customEmacs.homeManager =
16- { pkgs, ... }:
17- {
18- programs.emacs.enable = true;
19- programs.emacs.package = pkgs.emacs30-nox;
20- };
21- in
22- [
23- # from local bindings.
24- customEmacs
25- # from the aspect tree, cooper example is defined bellow
26- den.aspects.cooper
27- den.aspects.setHost
28- # from the `eg` namespace.
29- eg.autologin
30- # den included batteries that provide common configs.
31- <den/primary-user> # alice is admin always.
32- (<den/user-shell> "fish") # default user shell
33- ];
34-35- # Alice configures NixOS hosts it lives on.
36- nixos =
37- { pkgs, ... }:
38- {
39- users.users.alice.packages = [ pkgs.vim ];
40- };
41-42- # Alice home-manager.
43- homeManager =
44- { pkgs, ... }:
45- {
46- home.packages = [ pkgs.htop ];
47- };
48-49- # <user>.provides.<host>, via eg/routes.nix
50- provides.igloo =
51- { host, ... }:
52- {
53- nixos.programs.nh.enable = host.name == "igloo";
54- };
55- };
56-57- # This is a context-aware aspect, that emits configurations
58- # **anytime** at least the `user` data is in context.
59- # read more at https://vic.github.io/den/context-aware.html
60- den.aspects.cooper =
61- { user, ... }:
62- {
63- nixos.users.users.${user.userName}.description = "Alice Cooper";
64- };
65-66- den.aspects.setHost =
67- { host, ... }:
68- {
69- networking.hostName = host.hostName;
70- };
71-}
···1-{
2- config,
3- # deadnix: skip # enable <den/brackets> syntax for demo.
4- __findFile ? __findFile,
5- den,
6- ...
7-}:
8-{
9- # Lets also configure some defaults using aspects.
10- # These are global static settings.
11- den.default = {
12- darwin.system.stateVersion = 6;
13- nixos.system.stateVersion = "25.05";
14- homeManager.home.stateVersion = "25.05";
15- };
16-17- # These are functions that produce configs
18- den.default.includes = [
19- # ${user}.provides.${host} and ${host}.provides.${user}
20- <eg/routes>
21-22- # Enable home-manager on all hosts.
23- <den/home-manager>
24-25- # Automatically create the user on host.
26- <den/define-user>
27-28- # Disable booting when running on CI on all NixOS hosts.
29- (if config ? _module.args.CI then <eg/ci-no-boot> else { })
30-31- # NOTE: be cautious when adding fully parametric functions to defaults.
32- # defaults are included on EVERY host/user/home, and IF you are not careful
33- # you could be duplicating config values. For example:
34- #
35- # # This will append 42 into foo option for the {host} and for EVERY {host,user}
36- # ({ host, ... }: { nixos.foo = [ 42 ]; }) # DO-NOT-DO-THIS.
37- #
38- # # Instead try to be explicit if a function is intended for ONLY { host }.
39- (den.lib.take.exactly (
40- { OS, host }:
41- den.lib.take.unused OS {
42- nixos.networking.hostName = host.hostName;
43- }
44- ))
45-46- ];
47-}
···1-{
2- # autologin is context-aware, parametric aspect.
3- # it applies only if the context has at least { user }
4- # meaning that has access to user data
5- eg.autologin =
6- { user, ... }:
7- {
8- nixos =
9- { config, lib, ... }:
10- lib.mkIf config.services.displayManager.enable {
11- services.displayManager.autoLogin.enable = true;
12- services.displayManager.autoLogin.user = user.userName;
13- };
14- };
15-}
···1-# This example implements an aspect "routing" pattern.
2-#
3-# Unlike `den.default` which is `parametric.atLeast`
4-# we use `parametric.fixedTo` here, which help us
5-# propagate an already computed context to all includes.
6-#
7-# This aspect, when installed in a `parametric.atLeast`
8-# will just forward the same context.
9-# The `mutual` helper returns an static configuration which
10-# is ignored by parametric aspects, thus allowing
11-# non-existing aspects to be just ignored.
12-#
13-# Be sure to read: https://vic.github.io/den/dependencies.html
14-# See usage at: defaults.nix, alice.nix, igloo.nix
15-#
16-{ den, ... }:
17-{
18- # Usage: `den.default.includes [ eg.routes ]`
19- eg.routes =
20- let
21- inherit (den.lib) parametric;
22-23- # eg, `<user>._.<host>` and `<host>._.<user>`
24- mutual = from: to: den.aspects.${from.aspect}._.${to.aspect} or { };
25-26- routes =
27- { host, user, ... }@ctx:
28- parametric.fixedTo ctx {
29- includes = [
30- (mutual user host)
31- (mutual host user)
32- ];
33- };
34- in
35- routes;
36-}
···1+{ inputs, ... }:
2{
003 imports = [
4 (inputs.flake-file.flakeModules.dendritic or { })
5 (inputs.den.flakeModules.dendritic or { })
6 ];
7+8+ # other inputs may be defined at a module using them.
9+ flake-file.inputs = {
10+ den.url = "github:vic/den";
11+ flake-file.url = "github:vic/flake-file";
12+ home-manager = {
13+ url = "github:nix-community/home-manager";
14+ inputs.nixpkgs.follows = "nixpkgs";
15+ };
16+ };
17}
+19
templates/default/modules/hosts.nix
···0000000000000000000
···1+# defines all hosts + users + homes.
2+# then config their aspects in as many files you want
3+{
4+ # tux user at igloo host.
5+ den.hosts.x86_64-linux.igloo.users.tux = { };
6+7+ # define an standalone home-manager for tux
8+ # den.homes.x86_64-linux.tux = { };
9+10+ # be sure to add nix-darwin input for this:
11+ # den.hosts.aarch64-darwin.apple.users.alice = { };
12+13+ # other hosts can also have user tux.
14+ # den.hosts.x86_64-linux.south = {
15+ # wsl = { }; # add nixos-wsl input for this.
16+ # users.tux = { };
17+ # users.orca = { };
18+ # };
19+}
···1-# This repo was generated with github:vic/flake-file#dendritic template.
2-# Run `nix run .#write-flake` after changing any input.
3-#
4-# Inputs can be placed in any module, the best practice is to have them
5-# as close as possible to their actual usage.
6-# See: https://vic.github.io/dendrix/Dendritic.html#minimal-and-focused-flakenix
7-#
8-# For our template, we enable home-manager and nix-darwin by default, but
9-# you are free to remove them if not being used by you.
10-{ ... }:
11-{
12-13- flake-file.inputs = {
14- home-manager = {
15- url = "github:nix-community/home-manager";
16- inputs.nixpkgs.follows = "nixpkgs";
17- };
18-19- darwin = {
20- url = "github:nix-darwin/nix-darwin";
21- inputs.nixpkgs.follows = "nixpkgs";
22- };
23-24- ## these stable inputs are for wsl
25- #nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05";
26- #home-manager-stable.url = "github:nix-community/home-manager/release-25.05";
27- #home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable";
28-29- #nixos-wsl = {
30- # url = "github:nix-community/nixos-wsl";
31- # inputs.nixpkgs.follows = "nixpkgs-stable";
32- # inputs.flake-compat.follows = "";
33- #};
34-35- };
36-37-}
···0000000000000000000000000000000000000
-15
templates/default/modules/namespace.nix
···1-{ inputs, den, ... }:
2-{
3- # create an `eg` (example!) namespace. (flake exposed)
4- imports = [ (inputs.den.namespace "eg" true) ];
5-6- # you can have more than one namespace (false = not flake exposed)
7- # imports = [ (inputs.den.namespace "my" false) ];
8-9- # you can also merge many namespaces from remote flakes.
10- # keep in mind a namespace is defined only once, so give it an array:
11- # imports = [ (inputs.den.namespace "ours" [inputs.ours inputs.theirs]) ];
12-13- # this line enables den angle brackets syntax in modules.
14- _module.args.__findFile = den.lib.__findFile;
15-}
···1+{ den, ... }:
2+{
3+ # user aspect
4+ den.aspects.tux = {
5+ includes = [
6+ den.provides.primary-user
7+ (den.provides.user-shell "fish")
8+ ];
9+10+ homeManager =
11+ { pkgs, ... }:
12+ {
13+ home.packages = [ pkgs.htop ];
14+ };
15+16+ # user can provide NixOS configurations
17+ # to any host it is included on
18+ # nixos = { pkgs, ... }: { };
19+ };
20+}
-22
templates/default/modules/vm.nix
···1-# enables `nix run .#vm`. it is very useful to have a VM
2-# you can edit your config and launch the VM to test stuff
3-# instead of having to reboot each time.
4-{ inputs, eg, ... }:
5-{
6-7- den.aspects.igloo.includes = [
8- eg.vm._.gui
9- # eg.vm._.tui
10- ];
11-12- perSystem =
13- { pkgs, ... }:
14- {
15- packages.vm = pkgs.writeShellApplication {
16- name = "vm";
17- text = ''
18- ${inputs.self.nixosConfigurations.igloo.config.system.build.vm}/bin/run-igloo-vm "$@"
19- '';
20- };
21- };
22-}
···1+# Examples
2+3+This template provides some basic examples of how to use Den features.
4+However, you will learn more by reading templates/ci which tests all of Den.
5+6+Steps you can follow after cloning this template:
7+8+- Be sure to read the [den documentation](https://vic.github.io/den)
9+10+- Update den input.
11+12+```console
13+nix flake update den
14+```
15+16+- Run checks to test everything works.
17+18+```console
19+nix flake check
20+```
21+22+- Read [modules/den.nix](modules/den.nix) where hosts and homes definitions are for this example.
23+24+- Read [modules/namespace.nix](modules/namespace.nix) where a new `eg` (an example) aspects namespace is created.
25+26+- Read [modules/aspects/igloo.nix](modules/aspects/igloo.nix) where the `igloo` host is configured.
27+28+- Read [modules/aspects/alice.nix](modules/aspects/alice.nix) where the `alice` user is configured.
29+30+- Run the VM.
31+32+```console
33+nix run .#vm
34+```
35+36+- Edit and run VM loop.
37+38+Feel free to add more aspects, organize things to your liking.
···1+{ den, eg, ... }:
2+{
3+ den.aspects.alice = {
4+5+ # Alice can include other aspects.
6+ # For small, private one-shot aspects, use let-bindings like here.
7+ # for more complex or re-usable ones, define on their own modules,
8+ # as part of any aspect-subtree.
9+ includes =
10+ let
11+ # hack for nixf linter to keep findFile :/
12+ unused = den.lib.take.unused __findFile;
13+ __findFile = unused den.lib.__findFile;
14+15+ customEmacs.homeManager =
16+ { pkgs, ... }:
17+ {
18+ programs.emacs.enable = true;
19+ programs.emacs.package = pkgs.emacs30-nox;
20+ };
21+ in
22+ [
23+ # from local bindings.
24+ customEmacs
25+ # from the aspect tree, cooper example is defined bellow
26+ den.aspects.cooper
27+ den.aspects.setHost
28+ # from the `eg` namespace.
29+ eg.autologin
30+ # den included batteries that provide common configs.
31+ <den/primary-user> # alice is admin always.
32+ (<den/user-shell> "fish") # default user shell
33+ ];
34+35+ # Alice configures NixOS hosts it lives on.
36+ nixos =
37+ { pkgs, ... }:
38+ {
39+ users.users.alice.packages = [ pkgs.vim ];
40+ };
41+42+ # Alice home-manager.
43+ homeManager =
44+ { pkgs, ... }:
45+ {
46+ home.packages = [ pkgs.htop ];
47+ };
48+49+ # <user>.provides.<host>, via eg/routes.nix
50+ provides.igloo =
51+ { host, ... }:
52+ {
53+ nixos.programs.nh.enable = host.name == "igloo";
54+ };
55+ };
56+57+ # This is a context-aware aspect, that emits configurations
58+ # **anytime** at least the `user` data is in context.
59+ # read more at https://vic.github.io/den/context-aware.html
60+ den.aspects.cooper =
61+ { user, ... }:
62+ {
63+ nixos.users.users.${user.userName}.description = "Alice Cooper";
64+ };
65+66+ den.aspects.setHost =
67+ { host, ... }:
68+ {
69+ networking.hostName = host.hostName;
70+ };
71+}
···1+{
2+ config,
3+ # deadnix: skip # enable <den/brackets> syntax for demo.
4+ __findFile ? __findFile,
5+ den,
6+ ...
7+}:
8+{
9+ # Lets also configure some defaults using aspects.
10+ # These are global static settings.
11+ den.default = {
12+ darwin.system.stateVersion = 6;
13+ nixos.system.stateVersion = "25.05";
14+ homeManager.home.stateVersion = "25.05";
15+ };
16+17+ # These are functions that produce configs
18+ den.default.includes = [
19+ # ${user}.provides.${host} and ${host}.provides.${user}
20+ <eg/routes>
21+22+ # Enable home-manager on all hosts.
23+ <den/home-manager>
24+25+ # Automatically create the user on host.
26+ <den/define-user>
27+28+ # Disable booting when running on CI on all NixOS hosts.
29+ (if config ? _module.args.CI then <eg/ci-no-boot> else { })
30+31+ # NOTE: be cautious when adding fully parametric functions to defaults.
32+ # defaults are included on EVERY host/user/home, and IF you are not careful
33+ # you could be duplicating config values. For example:
34+ #
35+ # # This will append 42 into foo option for the {host} and for EVERY {host,user}
36+ # ({ host, ... }: { nixos.foo = [ 42 ]; }) # DO-NOT-DO-THIS.
37+ #
38+ # # Instead try to be explicit if a function is intended for ONLY { host }.
39+ (den.lib.take.exactly (
40+ { OS, host }:
41+ den.lib.take.unused OS {
42+ nixos.networking.hostName = host.hostName;
43+ }
44+ ))
45+46+ ];
47+}
···1+{
2+ # autologin is context-aware, parametric aspect.
3+ # it applies only if the context has at least { user }
4+ # meaning that has access to user data
5+ eg.autologin =
6+ { user, ... }:
7+ {
8+ nixos =
9+ { config, lib, ... }:
10+ lib.mkIf config.services.displayManager.enable {
11+ services.displayManager.autoLogin.enable = true;
12+ services.displayManager.autoLogin.user = user.userName;
13+ };
14+ };
15+}
···1+# This example implements an aspect "routing" pattern.
2+#
3+# Unlike `den.default` which is `parametric.atLeast`
4+# we use `parametric.fixedTo` here, which help us
5+# propagate an already computed context to all includes.
6+#
7+# This aspect, when installed in a `parametric.atLeast`
8+# will just forward the same context.
9+# The `mutual` helper returns an static configuration which
10+# is ignored by parametric aspects, thus allowing
11+# non-existing aspects to be just ignored.
12+#
13+# Be sure to read: https://vic.github.io/den/dependencies.html
14+# See usage at: defaults.nix, alice.nix, igloo.nix
15+#
16+{ den, ... }:
17+{
18+ # Usage: `den.default.includes [ eg.routes ]`
19+ eg.routes =
20+ let
21+ inherit (den.lib) parametric;
22+23+ # eg, `<user>._.<host>` and `<host>._.<user>`
24+ mutual = from: to: den.aspects.${from.aspect}._.${to.aspect} or { };
25+26+ routes =
27+ { host, user, ... }@ctx:
28+ parametric.fixedTo ctx {
29+ includes = [
30+ (mutual user host)
31+ (mutual host user)
32+ ];
33+ };
34+ in
35+ routes;
36+}
···1+# This repo was generated with github:vic/flake-file#dendritic template.
2+# Run `nix run .#write-flake` after changing any input.
3+#
4+# Inputs can be placed in any module, the best practice is to have them
5+# as close as possible to their actual usage.
6+# See: https://vic.github.io/dendrix/Dendritic.html#minimal-and-focused-flakenix
7+#
8+# For our template, we enable home-manager and nix-darwin by default, but
9+# you are free to remove them if not being used by you.
10+{ ... }:
11+{
12+13+ flake-file.inputs = {
14+ home-manager = {
15+ url = "github:nix-community/home-manager";
16+ inputs.nixpkgs.follows = "nixpkgs";
17+ };
18+19+ darwin = {
20+ url = "github:nix-darwin/nix-darwin";
21+ inputs.nixpkgs.follows = "nixpkgs";
22+ };
23+24+ ## these stable inputs are for wsl
25+ #nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05";
26+ #home-manager-stable.url = "github:nix-community/home-manager/release-25.05";
27+ #home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable";
28+29+ #nixos-wsl = {
30+ # url = "github:nix-community/nixos-wsl";
31+ # inputs.nixpkgs.follows = "nixpkgs-stable";
32+ # inputs.flake-compat.follows = "";
33+ #};
34+35+ };
36+37+}
+15
templates/example/modules/namespace.nix
···000000000000000
···1+{ inputs, den, ... }:
2+{
3+ # create an `eg` (example!) namespace. (flake exposed)
4+ imports = [ (inputs.den.namespace "eg" true) ];
5+6+ # you can have more than one namespace (false = not flake exposed)
7+ # imports = [ (inputs.den.namespace "my" false) ];
8+9+ # you can also merge many namespaces from remote flakes.
10+ # keep in mind a namespace is defined only once, so give it an array:
11+ # imports = [ (inputs.den.namespace "ours" [inputs.ours inputs.theirs]) ];
12+13+ # this line enables den angle brackets syntax in modules.
14+ _module.args.__findFile = den.lib.__findFile;
15+}
···1+# enables `nix run .#vm`. it is very useful to have a VM
2+# you can edit your config and launch the VM to test stuff
3+# instead of having to reboot each time.
4+{ inputs, eg, ... }:
5+{
6+7+ den.aspects.igloo.includes = [
8+ eg.vm._.gui
9+ # eg.vm._.tui
10+ ];
11+12+ perSystem =
13+ { pkgs, ... }:
14+ {
15+ packages.vm = pkgs.writeShellApplication {
16+ name = "vm";
17+ text = ''
18+ ${inputs.self.nixosConfigurations.igloo.config.system.build.vm}/bin/run-igloo-vm "$@"
19+ '';
20+ };
21+ };
22+}
···1-User TODO: REMOVE this directory (or disable its import from den.nix)
2-3-It is used to implement tests for all den features so we can validate at CI.
4-5-Use it as reference to see how den features are used, however, be aware that this might not be the best practices for file/aspect organization.
···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-}
···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-# USER TODO: Remove this file.
5-# suppose this file was auto-generated by nixos-generate-config or some other hardware tooling.
6-{ lib, ... }:
7-{
8- # used in CI to test this file was actually imported.
9- options.auto-imported = lib.mkOption {
10- readOnly = true;
11- type = lib.types.bool;
12- default = true;
13- };
14-}
···1-# tests for extending `den.base.*` modules with capabilities (options).
2-# these allow you to expend all hosts/users/homes with custom option
3-# that can later be used by aspects for providing features.
4-#
5-{ lib, ... }:
6-{
7-8- # This module is base for all host configs.
9- den.base.host =
10- { host, ... }:
11- {
12- options.capabilities.ssh-server = lib.mkEnableOption "Does host ${host.name} provide ssh?";
13- };
14-15- # This module is base for all user configs.
16- den.base.user =
17- { user, ... }:
18- {
19- options.isAdmin = lib.mkOption {
20- type = lib.types.bool;
21- default = user.name == "alice"; # only alice is always admin
22- };
23- };
24-25- # This module is base for all home configs.
26- # den.base.home = { home, ... }: { };
27-28- # This one is included on each host/user/home
29- # it cannot access host/user/home values since this conf is generic.
30- den.base.conf = {
31- options.foo = lib.mkOption {
32- type = lib.types.str;
33- default = "bar";
34- };
35- };
36-37- # Now hosts and users can set any option defined in base modules.
38- den.hosts.x86_64-linux.rockhopper = {
39- capabilities.ssh-server = true;
40- foo = "boo";
41- users.alice = {
42- # isAdmin = true; # alice is admin by default, nothing explicit here.
43- foo = "moo";
44- };
45- };
46-47- den.aspects.rockhopper.includes =
48- let
49- # An aspect can make use of these options to provide configuration.
50- sshCapable =
51- { host, ... }:
52- {
53- nixos.services.sshd.enable = host.capabilities.ssh-server;
54- homeManager.services.ssh-agent.enable = host.capabilities.ssh-server;
55- };
56- in
57- [ sshCapable ];
58-59- # CI checks
60- perSystem =
61- {
62- checkCond,
63- rockhopper,
64- alice-at-rockhopper,
65- ...
66- }:
67- {
68- checks.host-conf-rockhopper-sshd = checkCond "sshd enabled" (
69- rockhopper.config.services.sshd.enable == true
70- );
71- checks.host-conf-alice-sshd = checkCond "ssh-agent enabled" (
72- alice-at-rockhopper.services.ssh-agent.enable == true
73- );
74- };
75-76-}
···1-# User TODO: Remove this file.
2-{
3- # default aspect can be used for global static settings.
4- den.default = {
5- # static values.
6- darwin.system.stateVersion = 6;
7- nixos.system.stateVersion = "25.05";
8- homeManager.home.stateVersion = "25.05";
9-10- # these defaults are set for checking with CI.
11- nixos.programs.vim.enable = true;
12- darwin.programs.zsh.enable = true;
13- };
14-}
···1-# USER TODO: remove this file.
2-# copy any desired module to your ./modules and let it be auto-imported.
3-{ inputs, ... }:
4-{
5- imports = [
6- # The _example directory contains CI tests for all den features.
7- # use it as reference of usage, but not of best practices.
8- (inputs.import-tree ./_example)
9- ];
10-}
···1{ inputs, den, ... }:
2{
3+ # we can import this flakeModule even if we dont have flake-parts as input!
4 imports = [ inputs.den.flakeModule ];
56 den.hosts.x86_64-linux.igloo.users.tux = { };
7+8+ den.aspects.igloo = {
9+ nixos =
10+ { pkgs, ... }:
11+ {
12+ environment.systemPackages = [ pkgs.hello ];
13+ passthru = { };
14+ };
15+ };
16+17+ den.aspects.tux = {
18+ includes = [ den.provides.primary-user ];
19+ };
20}
-11
templates/noflake/modules/compat.nix
···1-{ inputs, lib, ... }:
2-{
3- # we can import this flakeModule even if we dont have flake-parts as input!
4- imports = [ inputs.den.flakeModule ];
5-6- # NOTE: Currently Den needs a top-level attribute where to place configurations,
7- # by default it is the `flake` attribute, even if Den uses no flake-parts at all.
8- options.flake = lib.mkOption {
9- type = lib.types.submodule { freeformType = lib.types.anything; };
10- };
11-}
···00000000000
+3-4
templates/noflake/modules/den.nix
···1{ inputs, ... }:
2{
0003 # tux user on igloo host.
4 den.hosts.x86_64-linux.igloo.users.tux = { };
5···8 nixos =
9 { pkgs, ... }:
10 {
11- # remove these for a real bootable host
12- boot.loader.grub.enable = false;
13- fileSystems."/".device = "/dev/fake";
14 passthru = { };
15-16 environment.systemPackages = [
17 pkgs.vim
18 ];
···1{ inputs, ... }:
2{
3+ # we can import this flakeModule even if we dont have flake-parts as input!
4+ imports = [ inputs.den.flakeModule ];
5+6 # tux user on igloo host.
7 den.hosts.x86_64-linux.igloo.users.tux = { };
8···11 nixos =
12 { pkgs, ... }:
13 {
00014 passthru = { };
015 environment.systemPackages = [
16 pkgs.vim
17 ];