Skip to content

Resolution Pipeline

When Den evaluates a host, it runs a resolution pipeline driven by policies and entities. Policies are directed edges that fan context out to downstream entity kinds; each entity kind binds behavior for its resolved context.

flowchart TD
  start["den.hosts.x86_64-linux.laptop"] --> host["host {host}"]
  host -->|"host-to-users"| user["user {host, user} (per user)"]
  host -->|"host-to-default"| hdef["default {host}"]
  user -->|"user-to-default"| udef["default {host, user}"]
  host -->|"host-to-hm-host"| hmhost["hm-host {host}"]
  hmhost -->|"hm-host-to-hm-user"| hmuser["hm-user {host, user}"]
  hmuser --> fwd["forward into home-manager.users.alice"]
  1. Host resolution

    For each entry in den.hosts.<system>.<name>, the pipeline creates a host scope. The host’s own aspect is resolved via den.schema.host.includes, binding owned configs for the host’s class.

  2. Core policies fan out

    Core policies (modules/policies/core.nix) define the fundamental traversal edges:

    PolicyFromToResolve
    host-to-usershostuserOne edge per host.users entry
    host-to-defaulthostdefaultIdentity (passes context through)
    user-to-defaultuserdefaultIdentity

    Each policy’s resolve function receives the current context and returns a list of downstream contexts. host-to-users fans out: one { host, user } pair per user declared on the host.

  3. Battery policies create derived entity kinds

    Batteries register additional policies that create derived entity kinds when their conditions are met. Each battery uses makeHomeEnv to produce policies for the new entity kinds:

    PolicyConditionTarget entity kind
    host-to-hm-hostHM enabled, host has homeManager-class usershm-host
    hm-host-to-hm-userPer homeManager-class userhm-user
    host-to-hjem-hosthjem enabled, host has hjem-class usershjem-host
    hjem-host-to-hjem-userPer hjem-class userhjem-user
    host-to-maid-hostnix-maid enabled, host has maid-class usersmaid-host
    maid-host-to-maid-userPer maid-class usermaid-user
    host-to-wsl-hostNixOS host with wsl.enablewsl-host

    The -host entity kind imports the battery’s OS module (e.g., home-manager.nixosModules.home-manager). The -user entity kind forwards the resolved user aspect into the appropriate namespace (e.g., home-manager.users.<name>).

  4. Deduplication

    The pipeline tracks a seen set keyed by context identity. When a scope is entered for the first time with a given context, the full aspect (owned configs + statics + parametric matches) is included. Subsequent visits with the same context key skip already-applied includes, preventing den.default configs from being applied twice when the same aspect appears at multiple entity kinds.

    Each policy transition creates an independent scope with its own dedup state, so entity kinds reached through different policies are isolated.

  5. Home configurations

    Standalone den.homes entries follow a separate path with their own core policy:

    flowchart TD
      home["den.homes.x86_64-linux.alice"] --> homestage["home {home}"]
      homestage -->|"home-to-default"| hdef["default {home}"]
      homestage --> hmc["homeConfigurations.alice"]

    Home scopes have no host in context, so policies and provides requiring { host } are not activated. The home-to-default policy still applies shared defaults.

  6. Output

    Flake-level policies (modules/policies/flake.nix) drive the final assembly. to-os-outputs resolves all hosts for a system; to-hm-outputs resolves all homes. Each resolved entity is instantiated (lib.nixosSystem, darwinSystem, or homeManagerConfiguration depending on class) and placed into flake.nixosConfigurations, flake.darwinConfigurations, or flake.homeConfigurations.

Contribute Community Sponsor