Skip to content

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 ];
};
};

Den auto-generates den.aspects entries from den.hosts, den.homes, and den.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.

Type: attrsOf aspectsType

Namespaced aspect collections. Each key is a namespace name, each value is a full aspectsType. Populated by den.namespace or by merging upstream denful flake outputs.

den.ful.myns = {
some-aspect = { nixos.services.foo.enable = true; };
};

Type: attrsOf raw

Raw flake output for publishing namespaces. Set automatically by den.namespace; consumed by downstream flakes that import your aspects.

An aspect is an attribute set with:

KeyPurpose
<class>Config merged into hosts/homes of that class
includesList of modules or functions dispatched by context
__functorAuto-generated by parametric; drives dispatch

Functions in includes receiving { class, aspect-chain } are static — evaluated once during aspect resolution. Functions receiving context arguments ({ host }, { user }, etc.) are parametric — evaluated per context during pipeline resolution.

meta.adapter (deprecated — use handleWith)

Section titled “meta.adapter (deprecated — use handleWith)”

An aspect can declare a subtree adapter via meta.adapter. The adapter is a function that receives the inherited adapter and returns a new one, enabling composition — a parent’s filter cannot be overridden by children.

Prefer handleWith for new code — it provides the same subtree transformation capability with clearer semantics and composability.

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 foo._.bar) has meta.provider = ["foo"]. Deeply nested providers accumulate: foo._.bar._.baz has meta.provider = ["foo" "bar"].

The meta.provider list can be used to distinguish aspects by origin during pipeline resolution.

This is the place for Den built-in batteries, reusable aspects that serve as basic utilities and examples.

See Batteries Reference.

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._.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 hosts
den.aspects.iceberg.includes = [
({ host }:
den._.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.

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:

  1. Collects all aspects referenced by the entity
  2. Compiles each aspect into an effectful computation (fx.send "resolve")
  3. Evaluates static includes and defers parametric includes until context widens
  4. Walks the policy graph to fan out into derived stages
  5. Extracts the class-specific config (e.g., nixos for NixOS hosts)
  6. Merges everything into the entity’s evalModules call
Contribute Community Sponsor