Skip to content

vic/den

Repository files navigation

Sponsor Vic Ask DeepWiki Dendritic Nix License CI Status

den - Aspect-oriented context-driven Dendritic Nix configurations.

den and vic's dendritic libs made for you with Love++ and AI--. If you like my work, consider sponsoring

At its core, Den is a library built on flake-aspects for activating configuration-aspects via context-transformation pipelines.

On top of the library, Den provides a framework for the NixOS/nix-Darwin/Home-Manager Nix domains.

Den embraces your Nix choices and does not impose itself. All parts of Den are optional and replaceable. Works with your current setup, with/without flakes, flake-parts or any other Nix module system.

Templates:

default: +flake-file +flake-parts +home-manager

minimal: +flakes -flake-parts -home-manager

noflake: -flakes +npins +lib.evalModules +nix-maid

microvm: MicroVM runnable-pkg and guests. custom ctx-pipeline.

example: cross-platform

ci: Each feature tested as code examples

bogus: Isolated test for bug reproduction

Examples:

vic/vix: author spends more time in Den itself (-flakes)

quasigod.xyz: beautiful organization (+flake-parts)

GitHub Search

❄️ Try it:

# Run virtio MicroVM from templates/microvm
nix run github:vic/den?dir=templates/microvm#runnable-microvm
# Run qemu VM from templates/example
nix run github:vic/den

Testimonials

Den takes the Dendritic pattern to a whole new level, and I cannot imagine going back.
@adda - Very early Den adopter after using Dendritic flake-parts and Unify.

I’m super impressed with den so far, I’m excited to try out some new patterns that Unify couldn’t easily do.
@quasigod - Author of Unify dendritic-framework, on adopting Den.

Massive work you did here!
@drupol - Author of “Flipping the Configuration Matrix” Dendritic blog post.

Thanks for the awesome library and the support for non-flakes… it’s positively brilliant!. I really hope this gets wider adoption.
@vczf - At #den-lib:matrix.org channel.

Den is a playground for some very advanced concepts. I’m convinced that some of its ideas will play a role in future Nix areas. In my opinion there are some raw diamonds in Den.
@Doc-Steve - Author of Dendritic Design Guide

Code example (OS configuration domain)

Defining hosts, users and homes.

den.hosts.x86_64-linux.lap.users.vic = {};
den.hosts.aarch64-darwin.mac.users.vic = {};
den.homes.aarch64-darwin.vic = {};
$ nixos-rebuild switch --flake .#lap
$ darwin-rebuild switch --flake .#mac
$ home-manager   switch --flake .#vic

Extensible Schemas for hosts, users and homes.

# extensible base modules for common, typed schemas
den.schema.user = { user, lib, ... }: {
  config.classes =
    if user.userName == "vic" then [ "hjem" "maid" ]
    else lib.mkDefault [ "homeManager" ];

  options.mainGroup = lib.mkOption { default = user.userName; };
};

Dendritic Multi-Platform Hosts

# modules/my-laptop.nix
{ den, inputs, ... }: {
  den.aspects.my-laptop = {
    # re-usable configuration aspects. Den batteries and yours.
    includes = [ den.provides.hostname den.aspects.work-vpn ];

    # regular nixos/darwin modules or any other Nix class
    nixos  = { pkgs, ... }: { imports = [ inputs.disko.nixosModules.disko ]; };
    darwin = { pkgs, ... }: { imports = [ inputs.nix-homebrew.darwinModules.nix-homebrew ]; };

    # Custom Nix classes. `os` applies to both nixos and darwin. contributed by @Risa-G.
    # See https://den.oeiuwq.com/guides/custom-classes/#user-contributed-examples
    os = { pkgs, ... }: {
      environment.systemPackages = [ pkgs.direnv ];
    };

    # host can contribute default home environments to all its users.
    homeManager.programs.vim.enable = true;
  };
}

Multiple User Home Environments

# modules/vic.nix
{ den, ... }: {
  den.aspects.vic = {
    # supports multiple home environments, eg: for migrating from homeManager.
    homeManager = { pkgs, ... }: { };
    hjem.files.".envrc".text = "use flake ~/hk/home";
    maid.kconfig.settings.kwinrc.Desktops.Number = 3;

    # user can contribute OS-configurations to any host it lives on
    darwin.services.karabiner-elements.enable = true;

    # user class forwards into {nixos/darwin}.users.users.<userName>
    user = { pkgs, ... }: {
      packages = [ pkgs.helix ];
      description = "oeiuwq";
    };

    includes = [
      den.provides.primary-user        # re-usable batteries
      (den.provides.user-shell "fish") # parametric aspects
      den.aspects.tiling-wm            # your own aspects
      den.aspects.gaming.provides.emulators
    ];
  };
}

Custom Dendritic Nix Classes

Custom classes is how Den implements homeManager, hjem, wsl, microvm support. You can use the very same mechanism to create your own classes.

# Example: A class for role-based configuration between users and hosts

roleClass =
  { host, user }:
  { class, aspect-chain }:
  den._.forward {
    each = lib.intersectLists (host.roles or []) (user.roles or []);
    fromClass = lib.id;
    intoClass = _: host.class;
    intoPath = _: [ ];
    fromAspect = _: lib.head aspect-chain;
  };

den.ctx.user.includes = [ roleClass ];

den.hosts.x86_64-linux.igloo = {
  roles = [ "devops" "gaming" ];
  users = {
    alice.roles = [ "gaming" ];
    bob.roles = [ "devops" ];
  };
};

den.aspects.alice = {
  # enabled when both support gaming role
  gaming = { pkgs, ... }: { programs.steam.enable = true; };
};

den.aspects.bob = {
  # enabled when both support devops role
  devops = { pkgs, ... }: { virtualisation.podman.enable = true; };

  # not enabled at igloo host (bob missing gaming role on that host)
  gaming = {};
};

Guarded Forwarding Classes

Forward guards allow feature-detection without mkIf/mkMerge cluttering.

Aspects can simply assign configurations into a class (here persys) from any file, without any mkIf/mkMerge cluttering. The logic for determining if the class takes effect is defined at a single place.

Example inspired by @Doc-Steve

# Aspects use the `persys` class without any conditional. And guard guarantees
# settings are applied **only** when impermanence module has been imported.
persys = { host }: den._.forward {
  each = lib.singleton true;
  fromClass = _: "persys";
  intoClass = _: host.class;
  intoPath = _: [ "environment" "persistance" "/nix/persist/system" ];
  fromAspect = _: den.aspects.${host.aspect};
  guard = { options, config, ... }: options ? environment.persistance;
};

# enable on all hosts
den.ctx.host.includes = [ persys ];

# aspects just attach config to custom class
den.aspects.my-laptop.persys.hideMounts = true;

User-defined Extensions to Den Framework.

See example template/microvm for an example of custom den.ctx and den.schema extensions for supporting Declarative MicroVM guests with automatic host-shared /nix/store.

den.hosts.x86_64-linux.guest = {};
den.hosts.x86_64-linux.host = {
  microvm.guests = [ den.hosts.x86_64-linux.guest ];
};

den.aspects.guest = {
  # propagated into host.nixos.microvm.vms.<name>;
  microvm.autostart = true;

  # guest supports all Den features.
  includes = [ den.provides.hostname ];
  # As MicroVM guest propagated into host.nixos.microvm.vms.<name>.config;
  nixos = { pkgs, ... }: { environment.systemPackages = [ pkgs.hello ]; };
};