Skip to content

Coming from...

This page helps you translate concepts you already know into Den’s model. Den does not replace NixOS, home-manager, or flake-parts — it sits on top of them and generates the same flake outputs you are used to.

NixOS conceptDen equivalent
lib.nixosSystem { modules = [...]; }den.hosts.<system>.<name> — Den calls nixosSystem for you
configuration.nix / module filesden.aspects.<name>.nixosaspects bundle per-class config
imports = [ ./module.nix ]includes = [ den.aspects.foo ] — typed references, not file paths
specialArgs = { ... }Parametric dispatch — { host, user }: { ... } — function args, not module args
lib.mkIf condition { ... }Context shape IS the condition — { host, user }: ... only runs when both exist
Per-host module directoriesAspects with owned configs per class — one aspect, multiple platforms
nixos-rebuild switch --flake .#hostSame command — Den produces nixosConfigurations as usual
home-manager conceptDen equivalent
homeManagerConfiguration { modules = [...]; }den.homes.<system>.<name> for standalone, or den.hosts.*.users.*.classes = ["homeManager"] for integrated
HM module filesden.aspects.<name>.homeManager — same module system, different attachment
home-manager switch --flake .#user@hostSame command — Den produces homeConfigurations
Separate HM and NixOS configs for one concernOne aspect configures both: { nixos = ...; homeManager = ...; }

The key difference is that a single aspect like gaming can carry both the NixOS system config (drivers, services) and the home-manager user config (dotfiles, apps). When Den resolves for a host, it extracts the nixos attrs; when it resolves for a home, it extracts the homeManager attrs. You write the concern once.

flake-parts conceptDen equivalent
perSystemden.schema.flake-system — flake-level policies fan out per system
flake.nixosConfigurationsden.hosts — Den generates flake outputs from entity declarations
mkFlakeinputs.den.flakeModule or inputs.den.denModule — Den is a flake-parts module
flakeModules for sharingden.ful namespaces — shareable aspect libraries

Den integrates with flake-parts, so the two are complementary. You can use perSystem alongside Den for outputs that do not involve host or user entities (packages, devShells, checks).

Why Den uses real functions, not module args

Section titled “Why Den uses real functions, not module args”

This is the key architectural difference.

NixOS modules look like functions ({ pkgs, config, ... }: { ... }) but their arguments come from _module.args and specialArgs, which are part of the module fixpoint. When config values depend on those args and those args depend on config, you get infinite recursion — a familiar pain point in complex NixOS setups.

Den aspects are actual functions that receive context before module evaluation begins:

{ host }: {
nixos.networking.hostName = host.hostName;
}

The host argument is plain data, resolved outside the module system. By the time NixOS evalModules runs, the aspect has already produced a concrete attrset. This means you can freely depend on entity data — host name, user list, system architecture — without risking infinite recursion.

Den also uses context shape for dispatch. A function that takes { host, user } is silently skipped in scopes where only { host } exists. No mkIf, no enable flags — the function signature declares when it applies. See Parametric dispatch for details.

Beyond mapping existing concepts, Den introduces:

  • Cross-class composition — one aspect configures nixos + homeManager + darwin in a single place.
  • Parametric dispatch — function argument shape determines when config applies, replacing conditionals.
  • Policy-driven topologypolicies define how entities relate (host-to-users, host-to-wsl, custom), and the graph is extensible.
  • Reusable batteries — common patterns like primary-user setup or user shell config ship as includable aspects via den.provides.*.
  • Shareable librariesden.ful namespaces let you publish and consume aspect collections across flakes.
  • Deployment commands (nixos-rebuild, home-manager switch) are unchanged.
  • Nix module syntax inside nixos = { ... } or homeManager = { ... } is standard — you use the same options you already know.
  • Flake inputs, overlays, and pkgs work the same way.
Contribute Community Sponsor