Choosing a Mechanism
den has several ways to get configuration from where you write it to where it applies. They overlap on purpose, which makes the first question “which one do I use?”. This page answers it by goal.
By goal
Section titled “By goal”| I want to… | Use | Guide |
|---|---|---|
| bundle aspects so one brings the others | includes = [ … ] on the aspect | Configure Aspects |
| configure one module system (NixOS, darwin, home-manager) | a class key: nixos, darwin, homeManager, … | Class Modules |
| vary config by host or user | a class module taking { host, ... }, or an aspect that is a function of { host, ... } | Parametric Aspects |
| apply an aspect to every host / user / home | den.schema.<kind>.includes, or den.default.includes for all | Schema reference |
| give an aspect per-host tunables | the settings pattern (a reserved settings key) | Aspect Settings |
| attach an aspect to entities by a rule (“every server gets X”) | a policy returning policy.include | Policies |
| include something only when other aspects are present | policy.when with hasAspect | den.aspects reference |
| create entities (users on a host, hosts in an environment) | a policy returning policy.resolve.to | Policy reference |
| send config from a host to its users, or from a user to its host | see Where Does My Config Land? | Mutual Config |
| change the host from a user aspect | the user class for the user’s own account; provides.<host> or provides.to-hosts for anything else, never untargeted nixos (#694) | Where Does My Config Land? |
write a new class that lands inside another (user → users.users.<name>) | den.classes.<name> plus a policy.route policy | Custom Nix Classes |
| share data (ports, addresses, extension lists) rather than modules | a quirk, plus pipes if it must cross scopes | Quirks & Pipes |
| offer optional parts of an aspect for others to pick | nested aspects, den.aspects.<aspect>.<name> | Configure Aspects |
| stop an aspect or policy from applying in a subtree | excludes | Policy reference |
Three pairs people mix up
Section titled “Three pairs people mix up”provides.to-users versus a policy
Section titled “provides.to-users versus a policy”Both deliver config from a host to its users. provides.to-users lives
inside the aspect that holds the behaviour; a policy lives beside it.
# provides: the relationship is written inside the behaviourden.aspects.igloo.provides.to-users.homeManager.programs.git.enable = true;
# policy: the behaviour is a plain aspect, the relationship is a ruleden.aspects.git.homeManager.programs.git.enable = true;den.policies.git-for-igloo-users = { host, user, ... }: lib.optional (host.name == "igloo") (den.lib.policy.include den.aspects.git);den.schema.user.includes = [ den.policies.git-for-igloo-users ];provides is shorter and stays supported. For new configurations prefer the
policy: the git aspect stays reusable anywhere, and “who gets it” is
visible as a rule instead of buried in one host’s aspect.
policy.route versus den.batteries.forward
Section titled “policy.route versus den.batteries.forward”Both move a custom class’s content into another class. forward is the
older battery with more options in one call; policy.route is the policy
effect it is built from. Den’s own os, user and wsl classes use
route. Reach for route in new code and for forward when you need one
of its extras (per-item each, adapter modules) in a single expression.
A quirk versus a class key
Section titled “A quirk versus a class key”A class key carries modules into one module system. A quirk carries
data (records, lists, packages) that any aspect can read and turn into
config for whatever module system it writes. If the thing you are sharing
is “open port 443”, that is data: a quirk lets the firewall aspect,
a monitoring aspect and a reverse proxy all read it. If it is
“set services.nginx.enable”, that is a module: use nixos.
A decision path
Section titled “A decision path”flowchart TD
Q1{"Is it NixOS / darwin / home-manager config?"}
Q1 -- "no, it's data others read" --> QK["quirk (+ pipes to cross scopes)"]
Q1 -- yes --> Q2{"Does it depend on which host or user?"}
Q2 -- yes --> PA["class module taking host / user, or a parametric aspect"]
Q2 -- no --> Q3{"Who should get it?"}
PA --> Q3
Q3 -- "one aspect's users" --> INC["includes"]
Q3 -- "every entity of a kind" --> SCH["den.schema.kind.includes / den.default"]
Q3 -- "entities matching a rule" --> POL["policy + policy.include"]
Q3 -- "only if other aspects are present" --> WHEN["policy.when + hasAspect"]
Q3 -- "the other side: host to users, user to host" --> WHERE["see Where Does My Config Land?"]
See also
Section titled “See also”- Core Principles — why den separates behaviour from assignment
- Where Does My Config Land? — scope rules for class content
- Policies and Policy Activation
- Quirks & Pipes