Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@

<img width="300" height="300" alt="den" src="https://github.com/user-attachments/assets/af9c9bca-ab8b-4682-8678-31a70d510bbb" />

- Dendritic: same concern, different classes and context-aware.
- Dendritic: each module configures **same** concern over **different** Nix classes.

- Small, [DRY](modules/aspects/provides/unfree.nix) & [`class`-generic](modules/aspects/provides/primary-user.nix) modules.
- Create [DRY](modules/aspects/provides/unfree.nix) & [`class`-generic](modules/aspects/provides/primary-user.nix) modules.

- [Parametric](modules/aspects/provides/define-user.nix) over `host`/`home`/`user`.

- [Share](templates/examples/modules/_profile/namespace.nix) aspects across systems & repos.
- [Share](templates/default/modules/namespace.nix) aspects across systems & repos.

- Bidirectional [dependencies](modules/aspects/dependencies.nix): user/host contributions.
- Context-aware [dependencies](modules/aspects/dependencies.nix): user/host contributions.

- [Routable](templates/default/modules/aspects/eg/routes.nix) configurations.

- Custom factories for any Nix `class`.

Expand All @@ -38,7 +40,11 @@

- [Batteries](modules/aspects/provides/): Opt-in, replaceable aspects.

- [Well-tested](templates/examples/modules/_example/ci) with [examples](templates/examples).
- Opt-in [`<angle/brackets>`](https://vic.github.io/den/angle-brackets.html) aspect resolution.

- Templates [tested](templates/default/modules/tests.nix) along [examples](templates/examples/modules/_example/ci).

- Concepts [documented](https://vic.github.io/den).

Need more batteries? See [vic/denful](https://github.com/vic/denful).

Expand All @@ -52,15 +58,21 @@ See schema in [`_types.nix`](modules/_types.nix).

```nix
# modules/hosts.nix
# OS & standalone homes share 'vic' aspect.
# $ nixos-rebuild switch --flake .#my-laptop
# $ home-manager switch --flake .#vic
{
den.hosts.x86-64-linux.laptop.users.vic = {};
# same home-manager vic configuration
# over laptop, macbook and standalone-hm
den.hosts.x86_64-linux.lap.users.vic = {};
den.hosts.aarch64-darwin.mac.users.vic = {};
den.homes.aarch64-darwin.vic = {};
}
```

```console
$ nixos-rebuild switch --flake .#lap
$ darwin-rebuild switch --flake .#mac
$ home-manager switch --flake .#vic
```

🧩 [Aspect-oriented](https://github.com/vic/flake-aspects) incremental features. ([example](templates/default/modules/den.nix))

Any module can contribute configurations to aspects.
Expand All @@ -87,7 +99,7 @@ Any module can contribute configurations to aspects.
# User contribs to host
nixos.users.users = {
vic.description = "oeiuwq";
}
};
includes = [
den.aspects.tiling-wm
den._.primary-user
Expand Down
19 changes: 12 additions & 7 deletions nix/den-brackets.nix
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# __findFile implementation to resolve deep aspects.
# inspired by https://fzakaria.com/2025/08/10/angle-brackets-in-a-nix-flake-world
#
# For user facing documentation, see:
# See templates/default/_profile/den-brackets.nix
# See templates/default/_profile/namespace.nix
{
lib,
config,
Expand All @@ -21,14 +17,23 @@ let
notFound = "Aspect not found: ${lib.concatStringsSep "." path}";

headIsDen = head == "den";
readFromDen = lib.getAttrFromPath tail config.den;
readFromDen = lib.getAttrFromPath ([ "den" ] ++ tail) config;

headIsAspect = builtins.hasAttr head config.den.aspects;
readFromAspects = lib.getAttrFromPath path config.den.aspects;
aspectsPath = [
"den"
"aspects"
] ++ path;
readFromAspects = lib.getAttrFromPath aspectsPath config;

headIsDenful = lib.hasAttrByPath [ "ful" head ] config.den;
denfulTail = if lib.head tail == "provides" then lib.tail tail else tail;
readFromDenful = lib.getAttrFromPath ([ head ] ++ denfulTail) config.den.ful;
denfulPath = [
"den"
"ful"
head
] ++ denfulTail;
readFromDenful = lib.getAttrFromPath denfulPath config;

found =
if headIsDen then
Expand Down
7 changes: 7 additions & 0 deletions templates/default/modules/aspects/alice.nix
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,12 @@
{
home.packages = [ pkgs.htop ];
};

# <user>.provides.<host>, via eg/routes.nix
provides.igloo =
{ host, ... }:
{
nixos.programs.nh.enable = host.name == "igloo";
};
};
}
3 changes: 3 additions & 0 deletions templates/default/modules/aspects/defaults.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

# These are functions that produce configs
den.default.includes = [
# ${user}.provides.${host} and ${host}.provides.${user}
<eg/routes>

# Enable home-manager on all hosts.
<den/home-manager>

Expand Down
53 changes: 53 additions & 0 deletions templates/default/modules/aspects/eg/routes.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# This example implements an aspect "routing" pattern.
#
# Unlike `den.default` which is `parametric.atLeast` we use `parametric.exactly` here
# to be more strict and prevent multiple values inclusion.
#
# Be sure to read: https://vic.github.io/den/dependencies.html
# See usage at: defaults.nix, alice.nix, igloo.nix
#
{ den, eg, ... }:
{
# Usage: `den.default.includes [ eg.routes ]`
eg.routes =
let
inherit (den.lib) parametric;

os-from-user =
{
user,
host,
# deadnix: skip
OS,
# deadnix: skip
fromUser,
}:
parametric { inherit user host; } (mutual user host);

hm-from-host =
{
user,
host,
# deadnix: skip
HM,
# deadnix: skip
fromHost,
}:
parametric { inherit user host; } (mutual host user);

mutual = from: to: {
includes = [
# eg, `<user>._.<host>` and `<host>._.<user>`
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The abbreviation "eg" should be "e.g." (with periods) when used to mean "for example" (from Latin "exempli gratia").

Suggested change
# eg, `<user>._.<host>` and `<host>._.<user>`
# e.g., `<user>._.<host>` and `<host>._.<user>`

Copilot uses AI. Check for mistakes.
(den.aspects.${from.aspect}._.${to.aspect} or { })
];
};

in
{
__functor = parametric.exactly;
includes = [
os-from-user
hm-from-host
];
};
}
7 changes: 7 additions & 0 deletions templates/default/modules/aspects/igloo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,12 @@
eg.vm-bootable
eg.xfce-desktop
];

# <host>.provides.<user>, via eg/routes.nix
provides.alice =
{ user, ... }:
{
homeManager.programs.helix.enable = user.name == "alice";
};
};
}
25 changes: 25 additions & 0 deletions templates/default/modules/tests.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Some CI checks to ensure this template always works.
# Feel free to adapt or remove when this repo is yours.
{ inputs, ... }:
{
perSystem =
{ pkgs, self', ... }:
let
checkCond = name: cond: pkgs.runCommandLocal name { } (if cond then "touch $out" else "");
apple = inputs.self.darwinConfigurations.apple.config;
igloo = inputs.self.nixosConfigurations.igloo.config;
alice-at-igloo = igloo.home-manager.users.alice;
vmBuilds = !pkgs.stdenvNoCC.isLinux || builtins.pathExists (self'.packages.vm + "/bin/vm");
iglooBuilds = !pkgs.stdenvNoCC.isLinux || builtins.pathExists (igloo.system.build.toplevel);
appleBuilds = !pkgs.stdenvNoCC.isDarwin || builtins.pathExists (apple.system.build.toplevel);
in
{
checks."igloo builds" = checkCond "igloo-builds" iglooBuilds;
checks."apple builds" = checkCond "apple-builds" appleBuilds;
checks."vm builds" = checkCond "vm-builds" vmBuilds;

checks."alice enabled igloo nh" = checkCond "alice.provides.igloo" igloo.programs.nh.enable;
checks."igloo enabled alice helix" =
checkCond "igloo.provides.alice" alice-at-igloo.programs.helix.enable;
};
}
14 changes: 2 additions & 12 deletions templates/examples/modules/_example/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
User TODO: REMOVE this directory (or disable its import from den.nix)

It is used to implement tests for all den feature so we can validate at CI.
It is used to implement tests for all den features so we can validate at CI.

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.

For a more "real-world" layout, see the `_profile` directory
which is somewhat inspired on the
dendritic implementation at [vic/vix](https://github.com/vic/vix/tree/8c8c7b8).

However, feel free to also not use any predefined layout, explore by yourself
and find out how things work for you. Be sure to share your insights with
the [community](https://github.com/vic/den/discussions)
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.
2 changes: 1 addition & 1 deletion templates/examples/modules/_example/ci/import-tree.nix
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# configures class-automatic module auto imports for hosts/users/homes.
# See documentation at modules/aspects/provides/import-tree.nix
{
# deadnix: skip # see _profile/den-brackets.nix
# deadnix: skip
__findFile ? __findFile,
...
}:
Expand Down
5 changes: 5 additions & 0 deletions templates/examples/modules/_example/ci/namespace.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{ inputs, den, ... }:
{
imports = [ (inputs.den.namespace "eg" false) ];
_module.args.__findFile = den.lib.__findFile;
}
12 changes: 0 additions & 12 deletions templates/examples/modules/_profile/README.md

This file was deleted.

36 changes: 0 additions & 36 deletions templates/examples/modules/_profile/den-brackets.nix

This file was deleted.

3 changes: 0 additions & 3 deletions templates/examples/modules/_profile/hosts.nix

This file was deleted.

This file was deleted.

67 changes: 0 additions & 67 deletions templates/examples/modules/_profile/namespace.nix

This file was deleted.

Loading
Loading