den.aspects
den.aspects
Section titled “den.aspects”An attribute set of aspects. Each aspect key names an aspect; its value is
an aspect set containing per-class deferred modules and optional includes and provides:
den.aspects = { dns = { nixos.services.resolved.enable = true; darwin.networking.dns = [ "1.1.1.1" ]; includes = [ ./dns ]; };};Auto-generation
Section titled “Auto-generation”Den auto-generates den.aspects entries from den.hosts, den.homes,
and the users declared under each host’s users. For every declared
host/home/user, an aspect is created with the appropriate class
configurations. You do not need to declare den.aspects manually unless
adding shared aspects.
den.ful
Section titled “den.ful”Type: attrsOf namespaceType (internal)
Namespaced aspect collections. Each key is a namespace name, each value is
a namespace container whose freeform keys are aspects (it also carries
schema, classes, and _). Populated by den.namespace or by merging
upstream denful flake outputs.
den.ful.myns = { some-aspect = { nixos.services.foo.enable = true; };};flake.denful
Section titled “flake.denful”Type: attrsOf raw
Raw flake output for publishing namespaces. Set automatically by
den.namespace; consumed by downstream flakes that import your aspects.
Aspect structure
Section titled “Aspect structure”An aspect is an attribute set. Keys are either class keys (matching a registered class), nested aspect keys, or one of these structural keys:
| Key | Type | Purpose |
|---|---|---|
<class> | module / config | Config merged into entities of that class |
includes | listOf provider | Providers (aspects, sub-aspects, functions) pulled into this aspect |
excludes | listOf unspecified | Aspects or policies excluded from this subtree |
provides / _ | submodule | Sub-aspect namespace (_ is an alias for provides) |
policies | policy registry | Named policy functions, activated by placing in includes |
meta | submodule | Attached metadata (handleWith, provider, collisionPolicy) |
classes | lazyAttrsOf raw | Class schemas declared by this aspect, merged into den.classes |
name / description | str | Aspect name and description |
The pipeline also attaches internal __-prefixed fields (e.g. __args on
parametric aspects, __functor to make merged aspects callable). These are
implementation details, not authoring surface.
Static vs parametric includes
Section titled “Static vs parametric includes”A plain module function in includes (module-system signature) is
static — emitted once during aspect resolution. A function requesting
scope context arguments ({ host }, { user }, { home }, etc.) is
parametric — deferred and re-resolved per context as scope widens. See
Parametric aspects.
meta.handleWith
Section titled “meta.handleWith”Type: nullOr (handler record or list of handler records). Default: null.
Resolution handlers for the aspect’s subtree (current). Handlers compose down the tree — a parent’s constraint cannot be overridden by children.
meta.provider
Section titled “meta.provider”Type: listOf str (internal, read-only). Default: the provider prefix.
Tracks the structural origin of an aspect as a path. Top-level aspects have
meta.provider = []. An aspect provided by foo (via foo.provides.bar or
its alias foo._.bar) has meta.provider = ["foo"]. Deeply nested
providers accumulate: foo._.bar._.baz has meta.provider = ["foo" "bar"].
The meta.provider list distinguishes aspects by origin during pipeline
resolution.
Class module collision policy
Section titled “Class module collision policy”A collision occurs when a flat-form class module declares an arg (e.g.
host) that the module system also provides — via specialArgs or
_module.args. The collision policy selects what happens. See
Class Modules for the flat-form mechanism.
Resolution levels
Section titled “Resolution levels”The policy is resolved from three levels, first match wins:
| Level | Where to set | Type / default | Scope |
|---|---|---|---|
| Aspect | meta.collisionPolicy on the aspect | nullOr (enum), default null | All class modules in that aspect |
| Entity | collisionPolicy on the entity schema (den.schema.<kind>) | nullOr (enum), default null | All uses of that entity kind |
| Global | den.config.classModuleCollisionPolicy | enum, default "error" | Entire flake |
A null at the aspect or entity level falls through to the next level.
The global level supplies the effective default of "error".
Strategy values
Section titled “Strategy values”The enum is the same at every level: "error", "class-wins",
"den-wins".
| Value | Behavior |
|---|---|
"error" | Throw an eval error (effective default) |
"den-wins" | Den value takes precedence; a warning is emitted |
"class-wins" | Module-system value takes precedence; a warning is emitted |
Setting collision policy
Section titled “Setting collision policy”# Aspect level — meta.collisionPolicyden.aspects.my-aspect.meta.collisionPolicy = "den-wins";
# Entity level — den.schema.<kind>.collisionPolicy (all hosts)den.schema.host.collisionPolicy = "den-wins";
# Global — den.config.classModuleCollisionPolicyden.config.classModuleCollisionPolicy = "den-wins";den.batteries (aliased as den.provides and den._)
Section titled “den.batteries (aliased as den.provides and den._)”This is the place for Den built-in batteries, reusable aspects that serve as basic utilities and examples.
See Batteries Reference.
Cross-context forwarding
Section titled “Cross-context forwarding”Entities (hosts, users, homes) expose .resolved — the aspect produced
by their context pipeline. Forwards can use this to pull configuration
from one entity into another without manually wiring context calls.
When den.batteries.forward is called without fromAspect, it defaults to
item.resolved, resolving the source entity through its own context
pipeline:
# Collect SSH host keys from all other hostsden.aspects.iceberg.includes = [ ({ host }: den.batteries.forward { each = lib.filter (h: h != host) (lib.attrValues den.hosts.${host.system}); fromClass = _: "ssh-host-key"; intoClass = _: host.class; # "nixos" or "darwin" intoPath = _: [ ]; } )];Each host in each is resolved via its .resolved attribute, and the
ssh-host-key class content is forwarded into the target’s OS config.
host.class is the OS class name ("nixos", "darwin"), not the
context type.
Class resolution
Section titled “Class resolution”Resolution is handled internally by the fx pipeline. den.lib.aspects.resolve still exists but is not a public API — call it directly only if you are building custom pipeline stages.
When the pipeline resolves aspects for an entity, it:
- Collects all aspects referenced by the entity
- Compiles each aspect into an effectful computation (
fx.send "resolve") - Evaluates static includes and defers parametric includes until context widens
- Walks the policy graph to fan out into derived stages
- Extracts the class-specific config (e.g.,
nixosfor NixOS hosts) - Merges everything into the entity’s
evalModulescall