···5566import { Aside } from '@astrojs/starlight/components';
7788-99-<Aside title="Use the Source, Luke" icon="github">
1010-[`nix/lib.nix`](https://github.com/vic/den/blob/main/nix/lib.nix) · Built on [flake-aspects](https://github.com/vic/flake-aspects)
88+<Aside title="Source" icon="github">
99+[`nix/lib.nix`](https://github.com/vic/den/blob/main/nix/lib.nix) --
1010+Built on [flake-aspects](https://github.com/vic/flake-aspects)
1111</Aside>
12121313-## The __functor Pattern
1313+## The `__functor` Pattern
14141515-In Nix, any attribute set with `__functor` can be called as a function:
1515+Any Nix attrset with `__functor` can be called as a function:
16161717```nix
1818let
···2323in counter 8 # => 50
2424```
25252626-The `__functor` receives `self` (the attrset) and an argument.
2626+The `__functor` receives `self` (the attrset itself) and returns a function.
27272828## Aspects as Functors
29293030-Every aspect in `flake-aspects` has a default `__functor`:
3030+Every aspect in flake-aspects has a default `__functor` that ignores context
3131+and returns itself. Den replaces this functor with one that inspects context
3232+parameters to decide what to produce:
31333234```nix
3333-{
3434- nixos = { a = 1; };
3535- __functor = self: _context: self; # ignores context
3636-}
3737-```
3535+# Default: returns self regardless of context
3636+{ nixos.foo = 1; __functor = self: _ctx: self; }
38373939-By default, it ignores context and returns itself. But you can replace
4040-`__functor` with one that **inspects** context:
4141-4242-```nix
4343-{
4444- nixos.foo = 24;
4545- __functor = self: context:
4646- if context ? host
4747- then self
4848- else { includes = [ fallback ]; };
3838+# Parametric: inspects context shape
3939+den.lib.parametric {
4040+ nixos.foo = 1;
4141+ includes = [
4242+ ({ host }: { nixos.networking.hostName = host.hostName; })
4343+ ({ user }: { homeManager.home.username = user.userName; })
4444+ ];
4945}
5046```
5147···55515652### Owned Configurations
57535858-Direct settings defined under a class name:
5454+Direct settings per Nix class:
59556056```nix
6157den.aspects.igloo = {
···67636864### Includes
69657070-Dependencies on other aspects — a directed graph:
6666+A list forming a dependency graph:
71677268```nix
7369den.aspects.igloo.includes = [
7474- # an static include
7575- { nixos.programs.vim.enable = true }
7070+ # Static: applied unconditionally
7171+ { nixos.programs.vim.enable = true; }
76727777- # function includes
7878- #
7979- # activates on both { host, user }, { host }
8080- ({ host, ... }: {
8181- nixos.time.timeZone = "UTC";
8282- })
7373+ # Parametric: applied when context matches argument shape
7474+ ({ host, ... }: { nixos.time.timeZone = "UTC"; })
83758484- # activates only on { host }
8585- (den.lib.take.exactly ({ host }: {
8686- nixos.networking.hostName = host.hostName;
8787- }))
8888-8989- # a reference to any other aspect
9090- den.aspects.tools._.editors
7676+ # Reference to another aspect
7777+ den.aspects.tools
9178];
9279```
93809494-Includes can be:
9595-- **Static aspects** — always included
9696-- **Functions** — [Parametric Aspects](/explanation/parametric) called with context, included when they match
9797-9881### Provides
9982100100-Nested sub-aspects forming a tree:
8383+Named sub-aspects scoped to this aspect, accessible via `den.aspects.igloo._.name`
8484+or `den.aspects.igloo.provides.name`:
1018510286```nix
103103-104104-den.aspects.gaming.provides.emulation = {
105105- nixos.programs.retroarch.enable = true;
106106-};
107107-8787+den.aspects.igloo.provides.gpu = { host, ... }:
8888+ lib.optionalAttrs (host ? gpu) {
8989+ nixos.hardware.nvidia.enable = true;
9090+ };
10891```
1099211093## Resolution
11194112112-When Den needs a NixOS module from an aspect, it calls flake-aspects API `.resolve`:
9595+Aspects are resolved for a specific class using the `resolve` API from flake-aspects:
1139611497```nix
115115-module = den.aspects.igloo.resolve {
116116- class = "nixos";
117117-};
118118-```
119119-120120-flake-aspects does the following to obtain the nixos module:
121121-122122-```mermaid
123123-graph TD
124124- Asp["den.aspects.igloo"]
125125-126126- Asp --> Resolve[".resolve { class = 'nixos' }"]
127127-128128- Resolve --> Owned["owned: nixos, darwin, homeManager"]
129129-130130- Resolve --> Inc["includes: gaming, tools._.editors, fn..."]
131131-132132- Owned --> Collect["Collect all 'nixos' attrs<br/>from aspect + transitive includes"]
133133-134134- Inc --> Collect
135135- Collect --> Module["Single merged Nix module"]
136136-137137- Collect --> Resolve
138138-9898+module = den.aspects.igloo.resolve { class = "nixos"; };
13999```
140100141141-This collects all `nixos` configs from the aspect and all its transitive
142142-includes into a single Nix module.
101101+Resolution collects all `nixos` attrs from owned configs, walks `includes`
102102+recursively, applies parametric dispatch, deduplicates, and produces a
103103+single `deferredModule` containing all NixOS configuration.
···11---
22-title: OS Context Pipeline
33-description: The complete data flow from host declaration to final configuration.
22+title: Context Pipeline
33+description: How host/user/home declarations transform into NixOS/Darwin/HM configurations.
44---
5566-77-import { Aside } from '@astrojs/starlight/components';
66+import { Steps, Aside } from '@astrojs/starlight/components';
8799-<Aside title="Use the Source, Luke" icon="github">
1010-[`modules/context/types.nix`](https://github.com/vic/den/blob/main/modules/context/types.nix) · [`modules/context/os.nix`](https://github.com/vic/den/blob/main/modules/context/os.nix) · HM: [`hm-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-os.nix) · [`hm-integration.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-integration.nix)
88+<Aside title="Source" icon="github">
99+[`modules/context/os.nix`](https://github.com/vic/den/blob/main/modules/context/os.nix) --
1010+[`modules/context/types.nix`](https://github.com/vic/den/blob/main/modules/context/types.nix) --
1111+[`modules/config.nix`](https://github.com/vic/den/blob/main/modules/config.nix)
1112</Aside>
12131313-## The NixOS/nix-Darwin Pipeline
1414+## Pipeline Overview
14151515-When Den evaluates a host configuration, data flows through a pipeline
1616-of context transformations. Here is the complete picture:
1616+When Den evaluates a host, it walks a pipeline of context transformations:
17171818```mermaid
1919-graph TD
2020- Host["den.hosts.x86_64-linux.igloo"]
2121- Host -->|"initial context"| CtxHost["ctx.host { host }"]
2222-2323- CtxHost -->|"_.host"| HA["den.aspects.igloo<br/>(fixedTo { host })"]
2424- CtxHost -->|"into.default"| CtxDef1["<b>ctx.default</b> { host }"]
2525- CtxHost -->|"into.user<br/>(per user)"| CtxUser["ctx.user { host, user }"]
2626- CtxHost -->|"into.hm-host<br/>(if HM detected)"| CtxHM["ctx.hm-host { host }"]
2727-2828- CtxUser -->|"_.user (self)"| UA["den.aspects.tux<br/>(fixedTo { host, user })"]
2929- CtxHost -->|"_.user (cross)"| XP["host aspect includes<br/>matching { host, user }"]
3030- CtxUser -->|"into.default"| CtxDef2["<b>ctx.default</b> { host, user }"]
3131-3232- CtxHM -->|"_.hm-host"| HMmod["Import HM module"]
3333- CtxHM -->|"into.hm-user<br/>(per HM user)"| CtxHMU["ctx.hm-user { host, user }"]
3434-3535- CtxHMU -->|"forward homeManager<br/>into host"| FW["home-manager.users.tux"]
3636-3737- CtxDef1 -->|"includes"| DI1["<b>den.default.{class}</b> ()<br/>den.default.includes<br/>(host-context funcs)"]
3838- CtxDef2 -->|"includes"| DI2["den.default.includes<br/>(user-context funcs)"]
1919+flowchart TD
2020+ start["den.hosts.x86_64-linux.laptop"] --> host["den.ctx.host {host}"]
2121+ host -->|"_.host"| owned["Owned config: fixedTo {host} aspects.laptop"]
2222+ host -->|"_.user"| userctx["atLeast aspects.laptop {host, user}"]
2323+ host -->|"into.user"| user["den.ctx.user {host, user} (per user)"]
2424+ user -->|"_.user"| userown["fixedTo {host,user} aspects.alice"]
2525+ host -->|"into.hm-host"| hmhost["den.ctx.hm-host {host}"]
2626+ hmhost -->|"import HM module"| hmmod["home-manager OS module"]
2727+ hmhost -->|"into.hm-user"| hmuser["den.ctx.hm-user {host, user}"]
2828+ hmuser -->|"forward homeManager class"| fwd["home-manager.users.alice"]
3929```
40304141-## Stage by Stage
3131+<Steps>
3232+1. Host Context
42334343-### 1. Host Entry
3434+ For each entry in `den.hosts.<system>.<name>`, Den creates a `{host}` context.
3535+ The host context type (`den.ctx.host`) contributes:
44364545-Den reads `den.hosts.x86_64-linux.igloo` and applies the initial context `den.ctx.host`:
3737+ - `_.host` -- Applies the host's own aspect with `fixedTo { host }`, making
3838+ all owned configs available for the host's class.
3939+ - `_.user` -- For each user, applies the host's aspect with `atLeast { host, user }`,
4040+ activating parametric includes that need both host and user.
46414747-```nix
4848-den.ctx.host { host = den.hosts.x86_64-linux.igloo; }
4949-```
4242+2. User Context
50435151-### 2. Host Aspect Resolution
4444+ `into.user` maps each `host.users` entry into a `{host, user}` context.
4545+ The user context type (`den.ctx.user`) contributes:
52465353-`den.ctx.host.provides.host` (aliased as `_.host`) locates `den.aspects.igloo`
5454-and fixes it to the host context. All owned configs and static includes from the
5555-host aspect are collected.
4747+ - `_.user` -- Applies the user's own aspect with `fixedTo { host, user }`.
56485757-### 3. Default configs (host-level)
4949+3. Derived Contexts
58505959-`den.ctx.host.into.default` produces `{ host }` for `den.ctx.default`, which
6060-activates `den.default.includes` functions matching `{ host, ... }`.
5151+ Batteries register additional `into.*` transformations on the host context:
61526262-### 4. User Enumeration
5353+ | Transition | Condition | Produces |
5454+ |---|---|---|
5555+ | `into.hm-host` | `host.home-manager.enable && hasHmUsers` | `{host}` hm-host |
5656+ | `into.hm-user` | Per HM-class user on hm-host | `{host, user}` hm-user |
5757+ | `into.wsl-host` | `host.class == "nixos" && host.wsl.enable` | `{host}` wsl-host |
5858+ | `into.hjem-host` | `host.hjem.enable && hasHjemUsers` | `{host}` hjem-host |
5959+ | `into.hjem-user` | Per hjem-class user | `{host, user}` hjem-user |
6060+ | `into.maid-host` | `host.nix-maid.enable && hasMaidUsers` | `{host}` maid-host |
6161+ | `into.maid-user` | Per maid-class user | `{host, user}` maid-user |
63626464-`den.ctx.host.into.user` maps over `host.users`, producing one
6565-`den.ctx.user { host, user }` per user.
6363+ Each derived context can contribute its own aspect definitions and import
6464+ the necessary OS-level modules (e.g., `home-manager.nixosModules.home-manager`).
66656767-### 5. User Aspect Resolution
6666+3. Deduplication
68676969-Two providers contribute to user context:
7070-- `den.ctx.user.provides.user` (self-provider) — locates the user's own aspect (`den.aspects.tux`)
7171-- `den.ctx.host.provides.user` (cross-provider) — the host's contribution, dispatching host aspect includes matching `{ host, user }`
6868+ `dedupIncludes` in `modules/context/types.nix` ensures:
72697373-### 6. Default configs (user-level)
7070+ - **First occurrence** of a context type uses `parametric.fixedTo`, which includes
7171+ owned configs + statics + parametric matches.
7272+ - **Subsequent occurrences** use `parametric.atLeast`, which only includes
7373+ parametric matches (owned/statics already applied).
74747575-`ctx.user.into.default` activates `den.default.includes` again, this time
7676-with `{ host, user }` — functions needing user context now match.
7777-Because `den.ctx.default` was already visited in stage 3, only parametric
7878-includes are dispatched (owned and static configs are **deduplicated**).
7575+ This prevents `den.default` configs from being applied twice when the same
7676+ aspect appears at multiple pipeline stages.
79778080-### 7. Home-Manager Detection
7878+4. Home Configurations
81798282-`den.ctx.host.into.hm-host` checks if the host has users with `"homeManager"`
8383-in their `classes` list and a supported OS. If so, it activates `den.ctx.hm-host`.
8080+ Standalone `den.homes` entries go through a separate path:
84818585-### 8. HM Module Import (host-level)
8282+ ```mermaid
8383+ flowchart TD
8484+ home["den.homes.x86_64-linux.alice"] --> homectx["den.ctx.home {home}"]
8585+ homectx --> resolve["fixedTo {home} aspects.alice"]
8686+ resolve --> hmc["homeConfigurations.alice"]
8787+ ```
86888787-`den.ctx.hm-host.provides.hm-host` imports the Home-Manager NixOS/Darwin module.
8989+ Home contexts have no host, so functions requiring `{ host }` are not activated.
9090+ Functions requiring `{ home }` run instead.
88918989-### 9. HM User config collection (user-level)
9292+5. Output
90939191-For each HM user, `ctx.hm-user` uses `den._.forward` to take
9292-`homeManager` class configs and insert them into
9393-`home-manager.users.<name>` on the host.
9494-9595-## Home-Manager Detection Criteria
9696-9797-`ctx.host.into.hm-host` does not always activate. It checks three conditions
9898-(see [`hm-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-os.nix)):
9999-100100-1. **OS class is supported** — the host's class is `nixos` or `darwin`
101101-2. **HM users exist** — at least one user has `"homeManager"` in their `classes`
102102-3. **HM module available** — `inputs.home-manager` exists, or the host has a custom `hm-module`
103103-104104-All three must be true. Hosts without users, or with only non-HM users, skip the entire HM pipeline.
105105-106106-## Automatic Deduplication
107107-108108-`den.default` (alias for `den.ctx.default`) is reached at **`{host}`**, **`{host, user}`**,
109109-and **`{home}`** context stages — once for the host context and once per user.
110110-111111-Den's `ctxApply` automatically deduplicates includes using a seen-set:
112112-113113-- **First visit** to a context type: includes **owned + static + parametric** configs via `fixedTo`
114114-- **Subsequent visits**: includes only **parametric** configs via `atLeast`
115115-116116-Additionally, for each context, two providers are called:
117117-- **Self-provider** (`provides.${name}`) — the target's own aspect lookup
118118-- **Cross-provider** (`source.provides.${target}`) — the source's contribution (if defined)
119119-120120-```mermaid
121121-graph TD
122122- H["ctx.host { host }"] -->|"into.default (1st visit)"| D1["ctx.default { host }<br/><b>fixedTo</b>: owned + statics + parametric<br/>+ self-provider + cross-provider"]
123123- H -->|"into.user"| U["ctx.user { host, user }"]
124124- U -->|"into.default (2nd visit)"| D2["ctx.default { host, user }<br/><b>atLeast</b>: parametric only<br/>+ self-provider + cross-provider"]
125125-```
126126-127127-This means:
128128-129129-- **Owned** configs and **static** includes from `den.default` appear **exactly once**
130130-- **Parametric** functions in `den.default.includes` still run at every stage
131131- (each may match different context shapes)
132132-- Options of `types.package` or `types.listOf` no longer get duplicate entries
133133-134134-<Aside>
135135-For parametric includes in `den.default.includes`, use `den.lib.take.exactly`
136136-to restrict which context stages a function matches:
137137-138138-```nix
139139-den.default.includes = [ den.aspects.foo ];
140140-den.aspects.foo = take.exactly ({ host }: { nixos.x = 1; });
141141-```
142142-143143-This function runs only with `{ host }` context, not with `{ host, user }`.
144144-</Aside>
145145-146146-## Standalone Home-Manager
147147-148148-For `den.ctx.home`, the pipeline is shorter:
149149-150150-```mermaid
151151-graph TD
152152- Home["den.homes.x86_64-linux.tux"]
153153- Home -->|"creates"| CtxHome["ctx.home { home }"]
154154- CtxHome -->|"_.home"| HomeA["den.aspects.tux<br/>(fixedTo { home })"]
155155- CtxHome -->|"into.default"| CtxDef["ctx.default { home }"]
156156-```
157157-158158-## den.default Is an Alias
159159-160160-`den.default` is an alias for `den.ctx.default`. When you write:
9494+ `modules/config.nix` collects all hosts and homes, calls `host.instantiate`
9595+ (defaults to `lib.nixosSystem`, `darwinSystem`, or `homeManagerConfiguration`
9696+ depending on class), and places results into `flake.nixosConfigurations`,
9797+ `flake.darwinConfigurations`, or `flake.homeConfigurations`.
16198162162-```nix
163163-den.default.homeManager.home.stateVersion = "25.11";
164164-den.default.includes = [ den._.define-user ];
165165-```
166166-167167-You are actually setting `den.ctx.default.homeManager...` and
168168-`den.ctx.default.includes`. This means `den.default` is a full
169169-context type — it has self-named providers, `into`, `includes`, and owned attributes.
170170-171171-### How den.default Receives Data
172172-173173-`host`, `user`, and `home` aspects **do not** include `den.default` directly.
174174-Instead, each context type transforms **into** `default`:
175175-176176-```nix
177177-den.ctx.host.into.default = lib.singleton; # passes { host }
178178-den.ctx.user.into.default = lib.singleton; # passes { host, user }
179179-den.ctx.home.into.default = lib.singleton; # passes { home }
180180-```
181181-182182-This means `den.default` is reached through the declarative context
183183-pipeline, not by direct inclusion. The data flowing into `den.default`
184184-is whatever the source context provides.
185185-186186-### Best Practices
187187-188188-`den.default` is useful for global settings like `home.stateVersion`.
189189-However, prefer attaching parametric includes to the appropriate
190190-context type instead:
191191-192192-| Instead of | Use |
193193-|-----------|-----|
194194-| `den.default.includes = [ hostFunc ]` | `den.ctx.host.includes = [ hostFunc ]` |
195195-| `den.default.includes = [ hmFunc ]` | `den.ctx.hm-host.includes = [ hmFunc ]` |
196196-| `den.default.nixos.x = 1` | `den.ctx.host.nixos.x = 1` |
197197-198198-## Why This Design?
199199-200200-Each context type is **independent** and **composable**. You can:
201201-202202-- Add new context types without modifying existing ones
203203-- Attach aspects to any stage of the pipeline
204204-- Create custom transformations for domain-specific needs
205205-- Override any built-in context behavior
206206-207207-The pipeline is not hardcoded — it's declared through `den.ctx` definitions
208208-that you can inspect, extend, and customize.
9999+</Steps>
···11---
22title: Context System
33-description: How den.ctx defines, transforms, and propagates context.
33+description: How den.ctx types define data flow between entities.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-99-<Aside title="Use the Source, Luke" icon="github">
1010-[`modules/context/types.nix`](https://github.com/vic/den/blob/main/modules/context/types.nix) · [`modules/context/os.nix`](https://github.com/vic/den/blob/main/modules/context/os.nix)
88+<Aside title="Source" icon="github">
99+[`modules/context/types.nix`](https://github.com/vic/den/blob/main/modules/context/types.nix) --
1010+[`modules/context/os.nix`](https://github.com/vic/den/blob/main/modules/context/os.nix)
1111</Aside>
12121313-## What Is a Context?
1313+## What is a Context
14141515-In Den, a **context** is an attribute set whose **names** (not values) determine
1616-which functions get called. When Den applies a context `{ host, user }` to a
1717-function `{ host, ... }: ...`, the function matches. A function `{ never }: ...`
1818-does not match and is ignored.
1515+A **context** is a named stage in Den's evaluation pipeline. Each context type is declared
1616+under `den.ctx.<name>` and carries:
19172020-## Why Named Contexts?
1818+- **Aspect definitions** (`provides.<name>`) -- what this context contributes to the resolved aspect.
1919+- **Transformations** (`into.<other>`) -- functions that produce new contexts from the current one.
2020+- **Provides** (`provides.<other>`) -- cross-context providers injected by other context definitions.
21212222-Named contexts `ctx.host { host }` and `ctx.hm-host { host }`
2323-hold the same data, but `hm-host` **guarantees** that home-manager support was
2424-validated. This follows the [**parse-don't-validate**](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) principle: you cannot
2525-obtain an `hm-host` context unless all detection criteria passed.
2222+## Built-in Context Types
26232724```mermaid
2828-graph LR
2929- H["den.ctx.host {host}"] -->|"hm-detect"| Check{"host OS supported by HM?<br/>host has HM users?<br/>inputs.home-manager exists?"}
3030- Check -->|"all true"| HMH["same data {host}<br/>as den.ctx.hm-host"]
3131- Check -->|"any false"| Skip["∅ skipped"]
3232- HMH -->|"guaranteed"| Use["HM pipeline<br/>proceeds safely"]
2525+flowchart TD
2626+ host["{host}"] -->|"into.user (per user)"| user["{host, user}"]
2727+ host -->|"into.hm-host (if HM enabled)"| hmhost["{host} hm-host"]
2828+ hmhost -->|"into.hm-user (per HM user)"| hmuser["{host, user} hm-user"]
2929+ host -->|"into.wsl-host (if WSL enabled)"| wslhost["{host} wsl-host"]
3030+ host -->|"into.hjem-host (if hjem enabled)"| hjemhost["{host} hjem-host"]
3131+ hjemhost -->|"into.hjem-user"| hjemuser["{host, user}"]
3232+ host -->|"into.maid-host (if maid enabled)"| maidhost["{host} maid-host"]
3333+ maidhost -->|"into.maid-user"| maiduser["{host, user}"]
3334```
34353535-## Context Types: den.ctx
3636+The framework defines these contexts in `modules/context/os.nix` and the various battery modules.
3737+Each battery (Home Manager, hjem, nix-maid, WSL) registers its own `into.*` transitions on the `host` context.
36383737-Each context type is defined in `den.ctx`. Context types are
3838-[aspect submodules](/explanation/aspects/) — they inherit `includes`, `provides`,
3939-`description`, and owned freeform configs from `flake-aspects`'s `aspect` type.
4040-**Den adds only the `into` option for context transformations**
3939+## Context Type Anatomy
4040+4141+A context type at `den.ctx.host`:
41424243```nix
4343-den.ctx.foobar = {
4444- description = "The {foo, bar} context";
4444+den.ctx.host = {
4545+ description = "OS";
45464646- # the aspect responsible for configuring the foobar context
4747- provides.foobar = { foo, bar }: den.aspects.${foo}._.${bar};
4747+ # The main aspect activated by this context
4848+ provides.host = { host }: den.aspects.${host.aspect};
48494949- # foobar contributions to the baz context
5050- provides.baz = { baz }: { };
5050+ # How this context contributes an aspect to other derived contexts
5151+ provides.user = { host, user }: den.aspects.other-aspect;
51525252- # cutting-point for including other aspects
5353- includes = [ /* parametric aspects */ ];
5454-5555- # context transformations
5656- into = {
5757- # how to produce baz context from a foobar one.
5858- baz = { foo, bar }: [{ baz = computeBaz foo bar; }];
5959- };
5353+ # How to derive other contexts from this one
5454+ into.user = { host }:
5555+ map (user: { inherit host user; }) (lib.attrValues host.users);
6056};
6157```
62585959+The `_` (or `provides`) attrset maps context names to functions that take the current
6060+context data and return aspect fragments. The `into` attrset maps to functions that
6161+produce lists of new context values.
63626464-| Component | Purpose |
6565-|-----------|---------|
6666-| `description` | Human-readable description |
6767-| `provides.${name}` | Self-named provider: given a context, find aspect responsible for configuration |
6868-| `provides.${target}` | Cross-provider: source's contribution to a target context (called per transformation) |
6969-| `includes` | Parametric aspects activated for this context (aspect cutting-point) |
7070-| `into` | Transformations fan-out into other context types |
6363+## Context Resolution
71647272-## Context Application
6565+When Den processes a host, it calls `den.ctx.host { host = ...; }`. This triggers:
73667474-A context type is callable — it's a functor:
6767+1. `collectPairs` walks the context type's `into.*` transitions recursively,
6868+ building a list of `{ ctx, ctxDef, source }` pairs.
6969+2. `dedupIncludes` processes these pairs, applying `parametric.fixedTo` for the
7070+ first occurrence of each context type and `parametric.atLeast` for subsequent
7171+ ones, preventing duplicate owned configs.
7272+3. The result is a flat list of aspect fragments merged into one `deferredModule`.
75737676-```nix
7777-aspect = den.ctx.foobar { foo = "hello"; bar = "world"; };
7878-```
7474+## Custom Contexts
79758080-When applied, `ctxApply` walks the full `into` graph depth-first,
8181-collecting `(source, ctxDef, ctx)` triples, then deduplicates:
8282-8383-1. **First visit** to a context type — `fixedTo`: owned + static + parametric dispatch
8484-2. **Subsequent visits** — `atLeast`: parametric dispatch only, **user must take care**
8585-3. **Self-named provider** (`provides.${name}`) is always called when context is used
8686-4. **Cross-provider** (`source.provides.${target}`) is called if the source defines one
8787-8888-```mermaid
8989-graph TD
9090- Apply["ctx.foobar { foo, bar }"]
9191- Apply --> Collect["collectPairs: walk into graph"]
9292- Collect --> Dedup["dedupIncludes: seen-set"]
9393- Dedup --> First["1st visit: fixedTo + self + cross"]
9494- Dedup --> Later["Nth visit: atLeast + self + cross"]
9595- First --> Result["merged includes list"]
9696- Later --> Result
9797-```
9898-9999-## Transformation Types
100100-101101-Transformations have the type `source → [ target ]` — they return a **list**.
102102-This enables two patterns:
103103-104104-```mermaid
105105-graph TD
106106- subgraph "Fan-out (one → many)"
107107- Host1["{host}"] -->|"into.user"| U1["{host, user₁}"]
108108- Host1 -->|"into.user"| U2["{host, user₂}"]
109109- Host1 -->|"into.user"| U3["{host, user₃}"]
110110- end
111111- subgraph "Conditional (one → zero or one)"
112112- Host2["{host}"] -->|"into.hm-host"| Gate{"detection<br/>gate"}
113113- Gate -->|"passes"| HM["{host} as hm-host"]
114114- Gate -->|"fails"| Empty["∅ empty list"]
115115- end
116116-```
117117-118118-**Fan-out** — one context producing many:
7676+You can define your own alternative context piplelines outside of `den.ctx.host` or
7777+create custom context types for domain-specific needs like cloud infrastructure:
1197812079```nix
121121-den.ctx.host.into.user = { host }:
122122- map (user: { inherit host user; }) (attrValues host.users);
123123-```
124124-125125-One host fans out to N user contexts.
126126-127127-**Conditional propagation** — zero or one:
128128-129129-```nix
130130-den.ctx.host.into.hm-host = { host }:
131131- lib.optional (isHmSupported host) { inherit host; };
132132-```
133133-134134-If the condition fails, the list is empty and no `hm-host` context is created.
135135-The data is the same `{ host }`, but the named context guarantees the validation
136136-passed.
137137-138138-## Contexts as Aspect Cutting-Points
139139-140140-Since context types **are** aspect submodules, they naturally have owned
141141-configs and `.includes`:
142142-143143-```nix
144144-den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true;
145145-146146-den.ctx.hm-host.includes = [
147147- ({ host, ... }: { nixos.home-manager.backupFileExtension = "bak"; })
148148-];
149149-```
150150-151151-This is like `den.default.includes` **but scoped** — it only activates for
152152-hosts with validated home-manager support. Use context includes to attach
153153-aspects to specific pipeline stages instead of the catch-all `den.default`.
154154-155155-## Extending Context Flow
156156-157157-You can define new context types or new transformations into existing contexts from any module:
158158-159159-```nix
160160-den.ctx.hm-host.into.foo = { host }: [ { foo = host.name; } ];
161161-den.ctx.foo._.foo = { foo }: { funny.names = [ foo ]; };
162162-```
163163-164164-The module system merges these definitions. You can extend the pipeline
165165-without modifying any built-in file.
166166-167167-## Built-in Context Types
168168-169169-Den defines these context types for its NixOS/Darwin/HM framework:
170170-171171-### den.ctx.host — `{ host }`
172172-173173-This is how NixOS configurations get created:
174174-175175-```nix
176176-# use den API to apply the context to data
177177-aspect = den.ctx.host {
178178- # value is `den.hosts.<system>.<name>`.
179179- host = den.hosts.x86_64-linux.igloo;
8080+den.ctx.my-service = {
8181+ description = "Custom service context";
8282+ provides.my-service = den.aspects.my-service;
18083};
18184182182-# use flake-aspects API to resolve nixos module
183183-nixosModule = aspect.resolve { class = "nixos"; };
8585+den.ctx.host.into.my-service = { host }:
8686+ lib.optional host.my-service.enable { inherit host; };
18487185185-# use NixOS API to build the system
186186-nixosConfigurations.igloo = lib.nixosSystem {
187187- modules = [ nixosModule ];
8888+den.aspects.my-service = { host }: {
8989+ nixos.services.my-service.hostName = host.hostName;
18890};
189189-```
19091191191-### den.ctx.user — `{ host, user }`
19292193193-A host fan-outs a new context for each user.
19493195195-### den.ctx.default
196196-197197-Aliased as `den.default`, used to define static global settings for hosts, users and homes.
198198-199199-### den.ctx.hm-host — `{ host }`
200200-201201-Home Manager enabled host.
202202-203203-A den.ctx.host gets transformed into den.ctx.hm-host only if the [host supports home-manager](/explanation/context-pipeline/).
204204-205205-When activated, it imports the HM module.
206206-207207-### den.ctx.hm-user — `{ host, user }`
208208-209209-For each user that has its class value set to homeManager.
210210-211211-When activated enables the `homeManager` user configuration class.
212212-213213-### den.ctx.home — `{ home }`
214214-215215-Entry point for standalone Home-Manager configurations.
216216-217217-## Custom Context Types
218218-219219-Create your own for domain-specific pipelines:
220220-221221-```nix
222222-den.ctx.greeting._.greeting = { hello }:
223223- { funny.names = [ hello ]; };
224224-225225-den.ctx.greeting.into.shout = { hello }:
226226- [{ shout = lib.toUpper hello; }];
227227-228228-den.ctx.shout._.shout = { shout }:
229229- { funny.names = [ shout ]; };
23094```
23195232232-Applying `den.ctx.greeting { hello = "world"; }` produces both
233233-`"world"` and `"WORLD"` through the transformation chain.
234234-235235-See the [Context Pipeline](/explanation/context-pipeline/) for the complete data flow.
236236-See the [`den.ctx` Reference](/reference/ctx/) for all built-in types.
9696+This creates a new stage in the pipeline that only activates for hosts
9797+with `my-service.enable = true`, and contributes NixOS config.
···11---
22title: Core Principles
33-description: The two fundamental ideas that make Den unique.
33+description: The two design principles behind Den.
44---
5566-## The Two Pillars
66+import { Aside } from '@astrojs/starlight/components';
77+88+## Feature-First, Not Host-First
99+1010+<Aside title="Recommended Read">
1111+[Flipping the Configuration Matrix](https://not-a-number.io/2025/refactoring-my-infrastructure-as-code-configurations/#flipping-the-configuration-matrix) by [Pol Dellaiera](https://github.com/drupol) was very influential in Den design and is a very recommended read.
71288-Den is built on two intertwined principles that together enable a new way
99-of thinking about Nix configurations:
1313+See also my [dendrix article](https://dendrix.oeiuwq.com/Dendritic.html) about the advantages of Dendritic Nix.
1414+</Aside>
1515+1616+Traditional Nix configurations start from hosts and push modules downward.
1717+Den follows a Dendritic model that inverts this: **aspects** (features) are the primary organizational unit.
1818+Each aspect declares its behavior per Nix class, and hosts simply select which
1919+aspects apply to them.
10201121```mermaid
1212-graph LR
1313- subgraph "1. Context Transformation"
1414- CT["Declarative context shapes"] --> TP["Pipeline of context transformations"]
2222+flowchart BT
2323+ subgraph "Aspect: bluetooth"
2424+ nixos["nixos: hardware.bluetooth.enable = true"]
2525+ hm["homeManager: services.blueman-applet.enable = true"]
1526 end
1616- subgraph "2. Context-Aware Aspects"
1717- CA["Functions of context"] --> CP["Provide Conditional Configs"]
1818- end
1919- CT -.-> CA
2020- CA -.-> CT
2727+ nixos --> laptop
2828+ nixos --> desktop
2929+ hm --> laptop
3030+ hm --> desktop
2131```
22322323-### 1. Context Transformation
3333+An aspect consolidates all class-specific configuration for a single concern.
3434+Adding bluetooth to a new host is one line: include the aspect.
3535+Removing it is deleting that line.
24362525-Traditional Nix setups wire modules directly: this NixOS module imports
2626-that file, this Home-Manager config hardcodes that username.
2727-2828-Den replaces this with a **declarative pipeline**. You declare entities
2929-(hosts, users, homes) and context types (shapes holding them). Den transforms these through
3030-a graph of context stages, each producing richer or conditioned data.
3737+## Context-Driven Dispatch
31383232-**the flow of data is declared separately from the configurations it produces**.
3939+Den uses function **parametric dispatch**:
4040+aspect functions declare which context parameters they need via their
4141+argument pattern.
33423434-### 2. Context-Aware Aspects
4343+```nix
4444+# Runs in every context (host, user, home)
4545+{ nixos.networking.firewall.enable = true; }
35463636-Aspects are not just bundles of configs — they are **functions** that inspect
3737-context to decide what to produce. A function taking `{ host, user }` naturally
3838-adapts to every host-user combination. A function requiring `{ other }` is
3939-not used.
4747+# Runs only when a {host} context exists
4848+({ host, ... }: { nixos.networking.hostName = host.hostName; })
40494141-**the shape of the function arguments determines when configs are produced, without explicit conditionals**.
5050+# Runs only when both {host, user} are present
5151+({ host, user, ... }: {
5252+ nixos.users.users.${user.userName}.extraGroups = [ "wheel" ];
5353+})
5454+```
42554343-## Why This Matters
5656+Den introspects function arguments at evaluation time. A function requiring
5757+`{ host, user }` is silently skipped in contexts that only have `{ host }`.
5858+No conditionals, no `mkIf`, no `enable` -- the context shape **is** the condition.
44594545-### Re-usability
6060+## Composition via Includes
46614747-An aspect like [`define-user`](/reference/batteries/#define-user) works on NixOS, Darwin, and standalone
4848-Home-Manager — not because it checks the platform, but because it produces
4949-configs for multiple classes and Den picks the relevant class module according to the configuration domain:
6262+Aspects form a directed acyclic graph through `includes` and form a tree of related aspects using `provides`.
50635164```nix
5252-{ host, user, ... }: {
5353- nixos.users.users.${user.userName}.isNormalUser = true;
5454- darwin.users.users.${user.userName}.home = "/Users/${user.userName}";
5555- homeManager.home.username = user.userName;
5656-}
6565+den.aspects.workstation = {
6666+ includes = [
6767+ den.aspects.dev-tools
6868+ den.aspects.gaming.provides.emulation
6969+ den.provides.primary-user
7070+ ];
7171+ nixos.services.xserver.enable = true;
7272+};
5773```
58745959-### Composability
60756161-Aspects compose through `includes` — a directed dependency graph.
6262-The [`parametric`](/explanation/parametric/) functor ensures context flows through the entire graph:
7676+## Separation of Concerns
63776464-```mermaid
6565-graph TD
6666- D["den.my-laptop"] --> WS["workspace"]
6767- D --> DU["common-users-env"]
6868- WS --> VPN["vpn"]
6969- WS --> DEV["dev-tools"]
7070- U["den.aspects.vic"] --> PU["primary-user"]
7171- U --> SH["user-shell 'fish'"]
7272-```
7373-7474-### Separation of Concerns
7575-7676-Each Dendritic file is focused on a single responsability. Configuring it across all different domains. Configurations are parametrized by data outside of their module config.
7878+Den separates **what exists** (schema) from **what it does** (aspects):
77797878-## The Result
8080+| Layer | Purpose | Example |
8181+|-------|---------|---------|
8282+| Schema | Declare entities | `den.hosts.x86_64-linux.laptop.users.alice = {}` |
8383+| Aspects | Configure behavior | `den.aspects.laptop.nixos.networking.hostName = "laptop"` |
8484+| Context | Transform data flow | `den.ctx.host` produces `{host}`, then `{host, user}` per user |
8585+| Batteries | Reusable patterns | `den.provides.primary-user`, `den.provides.user-shell` |
79868080-A system where:
8181-- **Declaring** entities is separate from **configuring** them
8282-- **Configurations** are separate from **when they apply**
8383-- [**Aspects**](/explanation/aspects/) compose without knowing each other’s details
8484-- [**Context**](/explanation/context-system/) flows declaratively through a [typed pipeline](/explanation/context-pipeline/)
8787+This separation means you can reorganize files, rename aspects, or add platforms
8888+without restructuring your configuration logic.
···11---
22title: Library vs Framework
33-description: Understanding Den's dual nature as both a library and a framework.
33+description: Using Den's core lib for any Nix domain beyond OS configuration.
44---
5566-77-## Den the Library
88-99-At its core, Den is a **library** for activating configuration aspects via context transformations.
1010-1111-The library provides:
1212-1313-- [**`den.ctx`**](/reference/ctx/) — Declarative context types with transformations
1414-- [**`den.lib.parametric`**](/reference/lib/#denlibparametric) — Functors for context-aware dispatch
1515-- [**`den.lib.take`**](/reference/lib/#denlibtake) — Argument matching (`atLeast`, `exactly`)
1616-- [**`den.lib.aspects`**](/reference/lib/#denlibaspects) — Aspect composition (via [flake-aspects](https://github.com/vic/flake-aspects))
1717-- [**`den.lib.canTake`**](https://github.com/vic/den/blob/main/nix/fn-can-take.nix) — Function signature introspection
1818-- [**`den.lib.__findFile`**](/guides/angle-brackets/) — Angle-bracket resolution
1919-2020-These tools work with **any Nix domain**. You can define context types
2121-for terraform modules, NixVim configs, container definitions, or anything
2222-else configurable through Nix.
66+import { Aside } from '@astrojs/starlight/components';
2372424-## Den the Framework
88+## Den as a Library
2592626-On top of the library, Den provides a **framework** for the common case
2727-of managing NixOS/Darwin/Home-Manager configurations:
2828-2929-- [**`den.hosts`**](/reference/schema/#denhosts) — Host declaration with freeform schema
3030-- [**`den.homes`**](/reference/schema/#denhomes) — Standalone Home-Manager declaration
3131-- [**`den.base`**](/reference/schema/#denbase) — Base modules for entities
3232-- [**`den.default`**](/explanation/context-pipeline/#dendefault-is-an-alias) — Global contributions to hosts / users / homes.
3333-- [**`den.provides`**](/reference/batteries/) — Batteries (define-user, unfree, etc.)
3434-- **Built-in `den.ctx` types** — [host, user, hm-host, hm-user, home](/reference/ctx/#built-in-context-types)
1010+Den's core (`nix/lib.nix`) is domain-agnostic. It provides:
35113636-The framework is the **first implementation** using the library, not
3737-the only possible one.
1212+| Function | Purpose |
1313+|---|---|
1414+| `parametric` | Wrap an aspect with context-aware dispatch |
1515+| `parametric.atLeast` | Match functions with at least the given params |
1616+| `parametric.exactly` | Match functions with exactly the given params |
1717+| `parametric.fixedTo` | Apply an aspect with a fixed context |
1818+| `parametric.expands` | Extend context before dispatch |
1919+| `canTake` | Check if a function accepts given arguments |
2020+| `take.atLeast` / `take.exactly` | Conditionally apply functions |
2121+| `statics` | Extract only static includes from an aspect |
2222+| `owned` | Extract only owned configs (no includes, no functor) |
2323+| `isFn` | Check if value is a function or has `__functor` |
2424+| `__findFile` | Angle bracket resolution for deep aspect paths |
2525+| `aspects` | The flake-aspects API (resolve, merge, types) |
38263939-## Using Den as a Library
2727+These primitives compose into context transformation pipelines for any domain.
40284141-Context types are independent of NixOS or OS configurations. Den can
4242-configure network topologies, declarative cloud infrastructure, container
4343-orchestration — anything describable as data transformations over
4444-Nix-configurable systems.
2929+## Using Den for Non-OS Domains
45304646-Here is a deployment pipeline example:
3131+Nothing about `den.lib` assumes NixOS, Darwin, or Home Manager. You can build
3232+context pipelines for any Nix module system:
47334834```nix
4949-den.ctx.deploy._.deploy = { target }:
5050- den.aspects.${target.name};
5151-5252-den.ctx.deploy.into.service = { target }:
5353- map (s: { service = s; target = target; })
5454- target.services;
3535+{ my-aspects, den, ... }: {
55365656-den.ctx.service._.service = { service, target }:
5757- { terraform.resource.${service.name} = { ... }; };
5858-```
3737+ # create a private isolated aspect namespace independent of den.aspects
3838+ imports = [ (den.namespace "my-aspects" false) ];
59396060-This creates a deployment pipeline where:
6161-1. Each deploy target has an aspect
6262-2. Targets enumerate their services
6363-3. Each service produces Terraform configurations
4040+ # Define aspects for a custom domain
4141+ my-aspects = {
4242+ web-server = den.lib.parametric {
4343+ terranix.resource.aws_instance.web = { ami = "..."; };
4444+ includes = [
4545+ # configures using the terranix Nix class
4646+ ({ env, ... }: { terranix.resource.aws_instance.web.tags.Env = env; })
4747+ ];
4848+ };
4949+ };
64506565-The same `ctxApply` mechanics — owned configs, self-named provider lookup,
6666-`into` transformations — drive this pipeline without any OS-specific code.
5151+ # Resolve for your custom class
5252+ aspect = my-aspects.web-server { env = "production"; };
5353+ module = aspect.resolve { class = "terranix"; };
5454+}
5555+```
67566868-You can even design and test custom context flows independently of
6969-building any configuration, using Den's test infrastructure.
5757+## Den as a Framework
70587171-Obtaining the final Nix configuration of `terraform` class is like:
5959+On top of the library, Den provides `modules/` which implement:
72607373-```nix
7474-aspect = den.ctx.deploy { target.name = "web"; };
6161+- **Schema types** (`den.hosts`, `den.homes`) for declaring NixOS/Darwin/HM entities
6262+- **Context pipeline** (`den.ctx.host`, `den.ctx.user`, `den.ctx.home`) for OS configurations
6363+- **Batteries** (`den.provides.*`) for common OS configuration patterns
6464+- **Output generation** (`modules/config.nix`) instantiating configurations into flake outputs
75657676-module = aspect.resolve { class = "terranix"; };
6666+The framework is entirely optional. You can use `den.lib` directly without
6767+any of the `den.hosts`/`den.aspects` machinery.
77687878-terranix.lib.terranixConfiguration {
7979- modules = [ module ];
8080-};
6969+```mermaid
7070+flowchart TD
7171+ subgraph "Den Library (domain-agnostic)"
7272+ parametric["parametric dispatch"]
7373+ canTake["canTake / take"]
7474+ aspects["flake-aspects API"]
7575+ end
7676+ subgraph "Den Framework (OS-specific)"
7777+ schema["den.hosts / den.homes"]
7878+ ctx["den.ctx pipeline"]
7979+ batteries["den.provides"]
8080+ output["flake output generation"]
8181+ end
8282+ parametric --> ctx
8383+ canTake --> parametric
8484+ aspects --> parametric
8585+ schema --> ctx
8686+ batteries --> ctx
8787+ ctx --> output
8188```
82898383-## No Lock-in
8484-8585-Because the framework is built on the library:
8686-8787-- You can use the library without the framework
8888-- You can replace any framework component
8989-- You can mix Den-managed and non-Den configs
9090-- You can extend the framework with custom context types
9191-- You can adopt incrementally — one host at a time
9090+## When to Use What
92919393-Den doesn't force a project structure, it plays well with anything you choose, be it flakes, npins, flake-parts, falake, or other module systems.
9292+- **Library only**: You have a custom Nix module system (Terranix, NixVim, system-manager)
9393+ and want parametric aspect dispatch without Den's host/user/home framework.
9494+- **Framework**: You configure NixOS/Darwin/Home Manager hosts and want the full
9595+ pipeline with batteries, schema types, and automatic output generation.
9696+- **Both**: Use the framework for OS configs and the library for additional domains
9797+ within the same flake.
+64-166
docs/src/content/docs/explanation/parametric.mdx
···11---
22title: Parametric Aspects
33-description: How parametric functors enable context forwarding and adaptation.
33+description: Context-aware function dispatch via argument introspection.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">
99-[`nix/lib.nix`](https://github.com/vic/den/blob/main/nix/lib.nix) · [`nix/fn-can-take.nix`](https://github.com/vic/den/blob/main/nix/fn-can-take.nix)
88+<Aside title="Source" icon="github">
99+[`nix/lib.nix`](https://github.com/vic/den/blob/main/nix/lib.nix) --
1010+[`nix/fn-can-take.nix`](https://github.com/vic/den/blob/main/nix/fn-can-take.nix)
1011</Aside>
11121212-## What Is a Parametric Aspect?
1313+## How Parametric Dispatch Works
13141414-A **parametric** aspect delegates its [implicit arguments](https://sngeth.com/functional%20programming/2024/09/25/point-free-style/) to functions defined in its `.includes`.
1515-1616-1717-The result of a `parametric` functions
1818-is an aspect that looks like this:
1515+Den inspects function arguments at evaluation time using `builtins.functionArgs`.
1616+A function's **required parameters** determine which contexts it matches:
19172018```nix
2121-foo = {
2222- # context propagation into includes
2323- __functor = self: ctx:
2424- map (f: f ctx) self.includes;
2525-2626- # owned configs
2727- nixos.foo = 1;
2828-2929- includes = [
3030- # functions receiving context
3131- (ctx: { nixos.bar = 2; })
3232- ];
3333-}
3434-```
1919+# Matches: {host}, {host, user}, {host, user, foo}
2020+({ host, ... }: { nixos.x = host.hostName; })
35213636-When applied `foo { x = 1; }` the context
3737-is propagated to the aspect includes.
2222+# Matches: {host, user} only
2323+(den.lib.take.exactly ({ host, user }: { nixos.x = user.userName; }))
38243939-Den provides several parametric functors in
4040-`den.lib.parametric`. Each of them provides a
4141-different `__functor` beaviour.
4242-4343-<Aside >
4444-Aspects created by Den from your host/user/home definitions are already `parametric`.
4545-They will forward context to their includes functions.
4646-4747-However for new aspects you define manually, they are not parametric unless you say so.
4848-If you get the following error:
4949-5050-```error
5151-error: function 'anonymous lambda' called without required argument 'user'
2525+# Matches: {home} **or** {home, foo}
2626+({ home }: { homeManager.x = home.userName; })
5227```
53285454-It likely means you are trying to forward context from an aspect that is not `parametric`.
5555-See also [#182](https://github.com/vic/den/discussions/182)
5656-</Aside>
2929+## The `canTake` Function
57305858-5959-## den.lib.parametric
6060-6161-The most common functor. When applied it
6262-includes the aspect owned config as well as
6363-the result of applying the context to
6464-included functions that support `atLeast` the
6565-same context.
6666-3131+`den.lib.canTake` (`nix/fn-can-take.nix`) checks if a function's required arguments
3232+are satisfiable with given parameters:
67336834```nix
6969-# NOTE: context is known until application
7070-foo = den.lib.parametric {
7171- # always included
7272- nixos.networking.hostName = "owned";
3535+canTake { host = ...; } ({ host, ... }: ...) # => true (atLeast)
3636+canTake { host = ...; } ({ host, user }: ...) # => false (user missing)
73377474- includes = [
7575- # context received here
7676- ({ host, ... }: { nixos.time.timeZone = "UTC"; })
7777- ];
7878-};
3838+canTake.exactly { host = ...; } ({ host }: ...) # => true
3939+canTake.exactly { host = ...; foo = ...; } ({ host, ... }: ...) # => false (extras)
7940```
80418181-When applied with `foo { host = ...; user = ...; }`:
4242+- `canTake.atLeast` -- all required params are present (default).
4343+- `canTake.exactly` -- required params match provided params exactly.
82448383-1. **Owned** configs (`nixos.networking.hostName`) are included
8484-2. **Static** includes are included
8585-3. **Functions** matching `atLeast` the context args are called
4545+## The `take` Utilities
86468787-## parametric.atLeast
8888-8989-Only dispatches to includes — does **not** contribute owned configs:
4747+`den.lib.take` wraps `canTake` into aspect-ready helpers:
90489149```nix
9292-foo = parametric.atLeast {
9393- nixos.ignored = 22;
9494- includes = [
9595- ({ x, ...}: { nixos.x = x; })
9696- ({ x, y }: { nixos.y = y; })
9797- ({ z }: { nixos.z = z; })
9898- ];
9999-};
100100-```
101101-102102-Applied with `foo { x = 1; y = 2; }`:
103103-- `{ x, ... }` matches (context has at least x)
104104-- `{ x, y }` matches (context has at least x y)
105105-- `{ z }` skipped (context has no z)
106106-107107-## parametric.exactly
108108-109109-Only dispatches to includes — does **not** contribute owned configs:
110110-111111-only calls functions with **exactly** matching args:
5050+# Apply fn only if ctx satisfies atLeast
5151+den.lib.take.atLeast fn ctx
11252113113-```nix
114114-foo = parametric.exactly {
115115- nixos.ignored = 22;
116116- includes = [
117117- ({ x, ...}: { nixos.x = x; })
118118- ({ x, y }: { nixos.y = y; })
119119- ({ z }: { nixos.z = z; })
120120- ];
121121-};
5353+# Apply fn only if ctx satisfies exactly
5454+den.lib.take.exactly fn ctx
12255```
12356124124-Applied with `{ x = 1; y = 2; }`:
125125-- `{ x, ... }` → skipped context is larget
126126-- `{ x, y }` → called (exact match)
127127-- `{ z }` → skipped no match
5757+These are used inside `includes` to control when a function activates.
128585959+## The `parametric` Constructor
12960130130-## parametric.fixedTo
131131-132132-This is an `atLeast` functor that also
133133-contributes its **owned** configs and
134134-ignores the context it is called with,
135135-replacing it with a fixed one.
6161+`den.lib.parametric` changes an aspect `__functor` which processes
6262+its `includes` list through parametric dispatch:
1366313764```nix
138138-foo = parametric.fixedTo { planet = "Earth"; } {
139139- nixos.foo = "contributed";
140140- # functions have atLeast semantics
6565+den.lib.parametric {
6666+ nixos.foo = 1; # owned config, always included
14167 includes = [
142142- ({ planet, ... }: { nixos.setting = planet; })
6868+ { nixos.bar = 2; } # static, always included
6969+ ({ host }: { nixos.x = host.name; }) # parametric, host contexts only
7070+ ({ user }: { homeManager.y = 1; }) # parametric, user contexts only
14371 ];
144144-};
7272+}
14573```
14674147147-No matter what context `foo` receives, its includes always get
148148-`{ planet = "Earth"; }`.
7575+When the aspect's `__functor` is called with a context, it filters `includes`
7676+based on argument compatibility and returns only matching entries.
14977150150-## parametric.expands
7878+<Aside type="caution" title="Anonymous functions are an anti-pattern">
7979+It is **not** recommended to have inlined functions on your `.includes` lists.
8080+This guide uses inlined functions only for examples, not as best-practice.
15181152152-Like `fixedTo` but
153153-adds attributes to the received context:
8282+Instead, use named aspects, this will improves readability and the error traces
8383+generated by Nix since those functions have a proper name and location.
1548415585```nix
156156-foo = parametric.expands { planet = "Earth"; } {
157157- includes = [
158158- ({ host, planet, ... }: {
159159- nixos.setting = "${host.name}/${planet}";
160160- })
161161- ];
162162-};
8686+den.aspects.my-laptop.includes = [ foo ];
8787+den.aspects.foo = { host }: { ... };
16388```
8989+</Aside>
16490165165-Applied with `{ host = ...; }`, the includes receive
166166-`{ host = ...; planet = "Earth"; }`.
9191+## Parametric Variants
9292+9393+| Constructor | Behavior |
9494+|---|---|
9595+| `parametric` | Default. <br/> Includes owned classes + static includes + function includes with context matching `atLeast`. |
9696+| `parametric.atLeast` | **Does NOT** include owned classes nor static includes. Only function matching atLeast the context. |
9797+| `parametric.exactly` | Like atLeast but using canTake.exactly for match. |
9898+| `parametric.fixedTo attrs` | Like `parametric` default but ignores any context and always uses the given attrs . |
9999+| `parametric.expands attrs` | Like `parametric`, but extends the received context with `attrs` before dispatch. |
167100168168-## parametric.withOwn
101101+## Example: Context-aware battery
169102170170-Combinator that adds owned config and static includes on top of any
171171-dispatch functor:
103103+This is a pattern used by many of our built-in batteries, be sure to see their code as example.
172104173105```nix
174174-parametric.withOwn parametric.atLeast {
175175- nixos.foo = "owned"; # included always
106106+den.lib.parametric.exactly {
176107 includes = [
177177- { nixos.bar = "static"; } # included always
178178- ({ host, ... }: { ... }) # dispatched via atLeast
108108+ ({ user, host }: { ... })
109109+ ({ home }: { ... })
179110 ];
180180-}
181181-```
182182-183183-The `parametric` function itself is an alias for `(parametric.withOwn parametric.atLeast)`.
184184-185185-186186-## Matching Rules Summary
187187-188188-| Functor | Owned configs | Statics includes | Functions includes semantics |
189189-|---------|:-----:|:-------:|:---------:|
190190-| `parametric` | ✓ | ✓ | atLeast |
191191-| `parametric.atLeast` | ✗ | ✗ | atLeast |
192192-| `parametric.exactly` | ✗ | ✗ | exactly |
193193-| `parametric.fixedTo ctx` | ✓ | ✓ | fixed ctx |
194194-| `parametric.expands ctx` | ✓ | ✓ | ctx + received |
195195-| `parametric.withOwn F` | ✓ | ✓ | uses F |
196196-197197-## take.exactly and take.atLeast
198198-199199-For individual functions (not whole aspects), use [`den.lib.take`](/reference/lib/#denlibtake):
200200-201201-202202-```nix
203203-foo = parametric.atLeast {
204204- includes = [
205205- (take.exactly ({ x, y }: ... ));
206206- (take.atLeast ({ x, y }: ... ));
207207- ];
208111};
209112```
210113211211-Applied with `foo { x = 1; y = 2; z = 3; }`:
212212-- `exactly { x, y }` is skipped
213213-- `atLeast { x, y }` matches
214214-215215-This mechanism prevents functions with
216216-lax context from matching everything,
217217-which would **produce duplicate config values**.
114114+The first function runs for every `{host, user}` context. The second only
115115+runs for `{home}` (standalone home-manager) contexts. No `mkIf` needed.
+43-50
docs/src/content/docs/guides/angle-brackets.mdx
···11---
22title: Angle Brackets Syntax
33-description: Opt-in shorthand for resolving deep aspect paths.
33+description: <aspect/path> shorthand via __findFile.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-99-<Aside title="Use the Source, Luke" icon="github">[`nix/den-brackets.nix`](https://github.com/vic/den/blob/main/nix/den-brackets.nix)</Aside>
88+<Aside title="Source" icon="github">
99+[`nix/den-brackets.nix`](https://github.com/vic/den/blob/main/nix/den-brackets.nix)
1010+</Aside>
10111111-<Aside type="caution">Angle brackets is an experimental, opt-in feature.</Aside>
1212+## What it Does
12131313-## What It Does
1414+Den provides a `__findFile` implementation that resolves `<aspect/path>`
1515+expressions into deep aspect lookups. This is syntactic sugar for
1616+accessing nested aspects and provides.
14171515-Den's `__findFile` resolves angle-bracket expressions to aspect paths:
1818+## Enabling
16191717-| Expression | Resolves to |
1818-|------------|-------------|
1919-| `<den.lib>` | `den.lib` |
2020-| `<den.default>` | `den.default` |
2121-| `<igloo>` | `den.aspects.igloo` |
2222-| `<foo/bar>` | `den.aspects.foo.provides.bar` |
2323-| `<foo/bar/baz>` | `den.aspects.foo.provides.bar.provides.baz` |
2424-| `<ns/tools>` | `den.ful.ns.tools` |
2525-2626-## Enable Globally
2727-2828-Create a module that sets it for all modules:
2020+Set `__findFile` via module args:
29213022```nix
3123{ den, ... }: {
···3325}
3426```
35273636-Then use it anywhere:
2828+## Resolution Rules
37293838-```nix
3939-{ __findFile, ... }: {
4040- den.default.includes = [ <den/define-user> ];
4141- den.aspects.igloo.includes = [ <foo/bar/baz> ];
4242-}
4343-```
3030+The `<name>` expression resolves through these paths in order:
3131+3232+1. **`<den.x.y>`** -- resolves to `config.den.x.y`
3333+2. **`<aspect>`** -- resolves to `config.den.aspects.aspect` (if `aspect` exists in `den.aspects`)
3434+3. **`<aspect/sub>`** -- resolves to `config.den.aspects.aspect.provides.sub`
3535+4. **`<namespace>`** -- resolves to `config.den.ful.namespace` (if it is a denful)
44364545-## Enable via Let-Binding
3737+The `/` separator is translated to `.provides.` in the lookup path.
46384747-For a single lexical scope:
3939+## Examples
48404941```nix
5050-{ den, ... }:
5151-let
5252- inherit (den.lib) __findFile;
5353-in {
5454- den.aspects.igloo.includes = [ <den/define-user> ];
5555-}
5656-```
4242+# Without angle brackets
4343+den.aspects.laptop.includes = [
4444+ den.aspects.tools.provides.editors
4545+ den.aspects.alice.provides.work-vpn
4646+ den.provides.primary-user
4747+];
57485858-## Namespace Access
5959-6060-With a namespace `ns` enabled:
6161-6262-```nix
6363-{ __findFile, ... }: {
6464- ns.moo.silly = true; # write access
6565- expr = <ns/moo>; # read resolves to den.ful.ns.moo
6666-}
4949+# With angle brackets
5050+den.aspects.laptop.includes = [
5151+ <tools/editors>
5252+ <alice/work-vpn>
5353+ <den.provides.primary-user>
5454+];
6755```
68566969-## Deep Nested Provides
5757+## When to Use
70587171-Slashes translate to `.provides.` in the aspect tree:
5959+Angle brackets are optional syntactic sugar. They are useful when:
6060+- You have deeply nested provides and want shorter references.
6161+- You are working with namespaces and want concise cross-references.
6262+6363+They are functionally identical to direct attribute access. The choice
6464+is a matter of style.
72657373-```nix
7474-den.aspects.foo.provides.bar.provides.baz.nixos.programs.fish.enable = true;
7575-den.aspects.igloo.includes = [ <foo/bar/baz> ];
7676-# igloo gets fish enabled
7777-```
6666+<Aside type="caution">
6767+Angle brackets use Nix's `__findFile` mechanism, which is positional
6868+(depends on being in scope). They only work in modules where
6969+`__findFile` has been set as a module argument.
7070+</Aside>
+97-57
docs/src/content/docs/guides/batteries.mdx
···11---
22title: Use Batteries
33-description: Opt-in, replaceable aspects for common tasks.
33+description: Built-in den.provides.* aspects for common tasks.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">
99-[`modules/aspects/provides/`](https://github.com/vic/den/tree/main/modules/aspects/provides) · Reference: [Batteries](/reference/batteries/)
88+<Aside title="Source" icon="github">
99+[`modules/aspects/provides/`](https://github.com/vic/den/tree/main/modules/aspects/provides)
1010</Aside>
11111212-## What Are Batteries?
1212+## What are Batteries
13131414-Batteries are pre-built aspects shipped with Den. They are **opt-in** — you
1515-explicitly include them where needed. They are **replaceable** — you can
1616-write your own alternative.
1414+Batteries are reusable aspects shipped with Den under `den.provides.*`
1515+(aliased as `den._.*`). They handle common cross-platform configuration
1616+patterns so you do not have to rewrite them.
17171818-Access batteries via `den._.` (shorthand for `den.provides.`).
1818+## Available Batteries
19192020-## define-user
2020+### `den.provides.define-user`
21212222-Defines a user at both OS and Home-Manager levels:
2222+Creates OS and home-level user account definitions:
23232424```nix
2525-den.default.includes = [ den._.define-user ];
2525+den.default.includes = [ den.provides.define-user ];
2626```
27272828-Sets `users.users.<name>`, `home.username`, and `home.homeDirectory`
2929-automatically for NixOS, Darwin, and standalone Home-Manager.
2828+Sets `users.users.<name>` on NixOS/Darwin and `home.username`/`home.homeDirectory`
2929+for Home Manager. Works in both host-user and standalone home contexts.
30303131-## primary-user
3131+### `den.provides.primary-user`
32323333-Makes a user an administrator:
3333+Marks a user as the primary user of the system:
34343535```nix
3636-den.aspects.vic.includes = [ den._.primary-user ];
3636+den.aspects.alice.includes = [ den.provides.primary-user ];
3737```
38383939-On NixOS: adds `wheel` and `networkmanager` groups.
4040-On Darwin: sets `system.primaryUser`.
4141-On WSL: sets `wsl.defaultUser` if host has a `wsl.enable = true`.
3939+- **NixOS**: adds `wheel` and `networkmanager` groups, sets `isNormalUser`.
4040+- **Darwin**: sets `system.primaryUser`.
4141+- **WSL**: sets `defaultUser` (if WSL is enabled).
42424343-## user-shell
4343+### `den.provides.user-shell`
44444545-Sets a user's default shell at OS and HM levels:
4545+Sets the default login shell at both OS and Home Manager levels:
46464747```nix
4848-den.aspects.vic.includes = [ (den._.user-shell "fish") ];
4848+den.aspects.alice.includes = [ (den.provides.user-shell "fish") ];
4949```
50505151-Enables the shell program on both NixOS/Darwin and Home-Manager,
5252-and sets it as the user's default shell.
5151+Enables `programs.<shell>.enable` on the OS and in Home Manager,
5252+and sets `users.users.<name>.shell`.
5353+5454+### `den.provides.forward`
5555+5656+Creates custom Nix classes by forwarding module contents into target
5757+submodule paths. See [Custom Nix Classes](/guides/custom-classes/) for details.
53585454-## unfree
5959+### `den.provides.import-tree`
55605656-Enables unfree packages by name:
6161+Recursively imports non-dendritic `.nix` files, auto-detecting class from
6262+directory names (`_nixos/`, `_darwin/`, `_homeManager/`):
57635864```nix
5959-den.aspects.my-laptop.includes = [ (den._.unfree [ "discord" "slack" ]) ];
6565+# Import per host
6666+den.ctx.host.includes = [ (den.provides.import-tree._.host ./hosts) ];
6767+6868+# Import per user
6969+den.ctx.user.includes = [ (den.provides.import-tree._.user ./users) ];
7070+7171+# Import for a specific aspect
7272+den.aspects.laptop.includes = [ (den.provides.import-tree ./disko) ];
6073```
61746262-Works for any class — NixOS, Darwin, Home-Manager.
7575+Requires `inputs.import-tree`.
63766464-## tty-autologin
7777+### `den.provides.unfree`
65786666-Enables automatic tty login for a given username:
7979+Enables specific unfree packages by name:
67806881```nix
6969-den.aspects.my-laptop.includes = [ (den._.tty-autologin "root") ];
8282+den.aspects.laptop.includes = [
8383+ (den.provides.unfree [ "nvidia-x11" "steam" ])
8484+];
7085```
71867272-## import-tree
8787+Works for any class (`nixos`, `darwin`, `homeManager`). The unfree predicate
8888+builder is automatically included via `den.default`.
73897474-Recursively imports non-dendritic Nix files by class:
9090+### `den.provides.tty-autologin`
9191+9292+Enables automatic TTY1 login on NixOS:
75937694```nix
7777-den.ctx.host.includes = [ (den._.import-tree._.host ./hosts) ];
7878-den.ctx.user.includes = [ (den._.import-tree._.user ./users) ];
9595+den.aspects.laptop.includes = [ (den.provides.tty-autologin "alice") ];
7996```
80978181-Given a directory structure like `./hosts/my-laptop/_nixos/`, it auto-imports
8282-files under the matching class directory. Useful for migration.
9898+### `den.provides.inputs'`
9999+100100+Provides flake-parts `inputs'` (system-specialized inputs) as a module argument:
101101+102102+```nix
103103+den.default.includes = [ den.provides.inputs' ];
104104+```
105105+106106+Requires flake-parts. Works in host, user, and home contexts.
831078484-## inputs' and self' (flake-parts)
108108+### `den.provides.self'`
851098686-Provide per-system `inputs'` and `self'` to NixOS/HM modules:
110110+Provides flake-parts `self'` (system-specialized self) as a module argument:
8711188112```nix
8989-den.default.includes = [ den._.inputs' den._.self' ];
113113+den.default.includes = [ den.provides.self' ];
114114+```
115115+116116+Requires flake-parts. Works in host, user, and home contexts.
117117+118118+## Usage Patterns
119119+120120+### Global Batteries
121121+122122+Apply to all entities via `den.default`:
901239191-den.aspects.igloo.nixos = { inputs', ... }: {
9292- environment.systemPackages = [ inputs'.nixpkgs.legacyPackages.hello ];
9393-};
124124+```nix
125125+den.default.includes = [
126126+ den.provides.define-user
127127+ den.provides.inputs'
128128+];
94129```
951309696-## forward
131131+### Per-Aspect Batteries
971329898-Create custom Nix classes that forward configs into target submodules:
133133+Apply to specific aspects:
99134100135```nix
101101-den.aspects.igloo.includes = [
102102- ({ class, aspect-chain }:
103103- den._.forward {
104104- each = lib.singleton class;
105105- fromClass = _: "custom";
106106- intoClass = _: "nixos";
107107- intoPath = _: [ ];
108108- fromAspect = _: lib.head aspect-chain;
109109- guard = { options, ... }: options ? custom; # optional
110110- })
136136+den.aspects.alice.includes = [
137137+ den.provides.primary-user
138138+ (den.provides.user-shell "zsh")
139139+ (den.provides.unfree [ "vscode" ])
111140];
112141```
113142114114-This is how Home-Manager, nix-maid, and hjem integrations are implemented internally.
115115-Use `guard` to forward only when target options exist.
143143+### Battery Composition
144144+145145+Batteries compose with regular aspects:
146146+147147+```nix
148148+den.aspects.my-admin = den.lib.parametric {
149149+ includes = [
150150+ den.provides.primary-user
151151+ (den.provides.user-shell "fish")
152152+ { nixos.security.sudo.wheelNeedsPassword = false; }
153153+ ];
154154+};
155155+```
···11---
22title: Configure Aspects
33-description: Recipes for configuring aspects — owned configs, includes, providers, and bidirectional dependencies.
33+description: Owned configs, includes, provides, and den.default.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">
88+<Aside title="Source" icon="github">
99[`modules/aspects/`](https://github.com/vic/den/tree/main/modules/aspects) · Tests: [`features/user-host-bidirectional-config.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/user-host-bidirectional-config.nix)
1010</Aside>
11111212-## Static Host Config
1212+## Aspect Basics
13131414-Set NixOS/Darwin/HM options directly on an aspect:
1414+An aspect is an attrset with per-class configs, an `includes` list, and
1515+optional `provides` sub-aspects:
15161617```nix
1717-den.aspects.igloo = {
1818- nixos = { pkgs, ... }: {
1919- environment.systemPackages = [ pkgs.hello ];
1818+{ den, ... }: {
1919+ den.aspects.laptop = {
2020+ # Owned configs per class
2121+ nixos = { pkgs, ... }: {
2222+ environment.systemPackages = [ pkgs.git ];
2323+ };
2424+ darwin.nix-homebrew.enable = true;
2525+ homeManager.programs.starship.enable = true;
2626+2727+ # Dependencies
2828+ includes = [
2929+ den.aspects.dev-tools
3030+ den.provides.primary-user
3131+ ];
3232+3333+ # Named sub-aspects
3434+ provides.gpu = { host, ... }:
3535+ lib.optionalAttrs (host ? gpu && host.gpu == "nvidia") {
3636+ nixos.hardware.nvidia.enable = true;
3737+ };
2038 };
2121- homeManager.programs.vim.enable = true;
2222-};
3939+}
2340```
24412525-## Include Other Aspects
4242+## Owned Configs
4343+4444+Attributes named after a Nix class (`nixos`, `darwin`, `homeManager`, or any
4545+custom class) are **owned configs**. They can be:
26462727-Compose aspects with `includes`:
4747+- **Attrsets**: merged directly into the class configuration.
4848+- **Functions**: called with the class's module arguments (`{ config, pkgs, lib, ... }`).
28492950```nix
3030-den.aspects.tux = {
3131- includes = [
3232- den.provides.primary-user
3333- (den.provides.user-shell "fish")
3434- den.aspects.base-tools
3535- ];
5151+# Attrset form
5252+den.aspects.laptop.nixos.networking.hostName = "laptop";
5353+5454+# Function form
5555+den.aspects.laptop.nixos = { pkgs, ... }: {
5656+ environment.systemPackages = [ pkgs.git ];
3657};
3758```
38593939-## Context-Aware Includes
6060+## Includes
40614141-Functions in `includes` receive context and only activate when they match:
6262+`includes` is a list of aspects, attrsets, or parametric functions:
42634364```nix
4444-den.aspects.igloo.includes = [
4545- den.aspects.video
4646-];
4747-4848-den.aspects.video = den.lib.take.exactly ({ host, user }: {
4949- nixos.users.users.${user.userName}.extraGroups = [ "video" ];
5050-});
6565+den.aspects.workstation.includes = [
6666+ # Reference another aspect (its full DAG is included)
6767+ den.aspects.dev-tools
51685252-```
6969+ # Static attrset (always applied)
7070+ { nixos.programs.vim.enable = true; }
53715454-This runs **once per user** on the host. A function taking `{ host }` only runs at the host level.
5555-5656-## Host Configures Its Users
5757-5858-A host aspect can contribute to user-level settings:
7272+ # Parametric function (applied when context shape matches)
7373+ ({ host, ... }: { nixos.networking.hostName = host.hostName; })
59746060-```nix
6161-den.aspects.igloo = {
6262- nixos.networking.hostName = "igloo";
6363- homeManager.programs.direnv.enable = true;
6464-};
7575+ # Battery
7676+ (den.provides.user-shell "fish")
7777+];
6578```
66796767-The `homeManager` config from a host aspect becomes the default for **all users** on that host.
6868-6969-## User Configures Its Host
8080+## Provides
70817171-A user aspect can contribute to the host's NixOS/Darwin config:
8282+`provides` creates named sub-aspects accessible via `den.aspects.<name>._.<sub>`
8383+or `den.aspects.<name>.provides.<sub>`:
72847385```nix
7474-den.aspects.tux = {
7575- nixos.users.users.tux.description = "cute penguin";
7676- homeManager = { pkgs, ... }: {
7777- home.packages = [ pkgs.htop ];
7878- };
8686+den.aspects.tools.provides.editors = {
8787+ homeManager.programs.helix.enable = true;
8888+ homeManager.programs.vim.enable = true;
7989};
9090+9191+# Used elsewhere:
9292+den.aspects.alice.includes = [ den.aspects.tools._.editors ];
8093```
81948282-The `nixos` config from a user aspect is applied to **every host** the user appears on.
8383-8484-8585-## Conditional Config by Host/User
8686-8787-Use aspect arguments to specialize:
9595+`provides` can also be parametric functions:
88968997```nix
9090-den.aspects.igloo.includes = [ den.aspects.tuxGit ];
9191-9292-den.aspects.tuxGit = den.lib.take.exactly ({ host, user }:
9393- if user.userName == "tux"
9494- then { homeManager.programs.git.enable = true; }
9595- else { }
9696-);
9898+den.aspects.alice.provides.work-vpn = { host, user, ... }:
9999+ lib.optionalAttrs host.hasVpn {
100100+ nixos.services.openvpn.servers.work.config = "...";
101101+ };
97102```
9810399104## Global Defaults
100105101101-Use `den.default` for settings that apply everywhere:
106106+`den.default` is a special aspect applied to every host, user, and home:
102107103108```nix
104104-den.default = {
105105- nixos.system.stateVersion = "25.11";
106106- homeManager.home.stateVersion = "25.11";
107107- includes = [
108108- den.provides.define-user
109109- (den.lib.take.exactly ({ host }: {
110110- nixos.networking.hostName = host.hostName;
111111- }))
112112- ];
113113-};
109109+{
110110+ den.default = {
111111+ nixos.system.stateVersion = "25.11";
112112+ homeManager.home.stateVersion = "25.11";
113113+ includes = [
114114+ den.provides.define-user
115115+ den.provides.inputs'
116116+ ];
117117+ };
118118+}
114119```
115120116116-Use `den.lib.take.exactly` to restrict a function to a specific context shape (e.g., only at host level, not user level).
121121+<Aside type="caution">
122122+Owned configs from `den.default` are deduplicated across pipeline stages.
123123+Parametric functions in `den.default.includes` are evaluated at every context
124124+stage. Use `den.lib.take.exactly` if a function should only run in specific contexts.
125125+</Aside>
117126118118-## Compose Named Sub-aspects
127127+## Bidirectional Flow
119128120120-Organize aspects hierarchically using `provides`:
129129+Users configure their hosts. Hosts configure their users. Aspects flow
130130+in both directions:
121131122132```nix
123123-den.aspects.gaming = {
124124- nixos.programs.steam.enable = true;
125125- provides.emulation.nixos.programs.retroarch.enable = true;
126126- provides.streaming = {
127127- nixos.services.sunshine.enable = true;
128128- homeManager.programs.moonlight.enable = true;
129129- };
133133+# User aspect contributes to host's NixOS config
134134+den.aspects.alice = {
135135+ nixos.users.users.alice.extraGroups = [ "docker" ];
136136+ homeManager = { pkgs, ... }: { home.packages = [ pkgs.htop ]; };
130137};
131138132132-den.aspects.my-pc.includes = [
133133- den.aspects.gaming
134134- den.aspects.gaming._.emulation
135135-];
139139+# Host aspect contributes to all its users' home config
140140+den.aspects.laptop = {
141141+ homeManager.programs.ssh.enable = true;
142142+ nixos.services.openssh.enable = true;
143143+};
136144```
+113-77
docs/src/content/docs/guides/custom-classes.mdx
···11---
22title: Custom Nix Classes
33-description: Create new Nix configuration classes with den._.forward.
33+description: Create new classes via den.provides.forward.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">[`modules/aspects/provides/forward.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/forward.nix) · [forward CI tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/forward-from-custom-class.nix)</Aside>
88+<Aside title="Source" icon="github">
99+[`modules/aspects/provides/forward.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/forward.nix) --
1010+[`modules/aspects/provides/os-user.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/os-user.nix) --
1111+[forward tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/forward-from-custom-class.nix)
1212+</Aside>
9131010-## What Are Custom Classes?
1414+## What is a Custom Class
11151212-Den natively supports `nixos`, `darwin`, and `homeManager` classes. But you
1313-can create your own classes that forward their settings into any target
1414-submodule. This is exactly how Home-Manager integration works internally.
1616+Den's built-in classes (`nixos`, `darwin`, `homeManager`) map to well-known
1717+NixOS module systems. But you can define **custom classes** that forward their
1818+contents into a target submodule path on another class.
15191616-## The forward Battery
2020+This is how Den implements:
2121+- The `user` class (forwards to `users.users.<name>` on the OS)
2222+- Home Manager integration (forwards `homeManager` to `home-manager.users.<name>`)
2323+- hjem integration (forwards `hjem` to `hjem.users.<name>`)
2424+- nix-maid integration (forwards `maid` to `users.users.<name>.maid`)
17251818-`den._.forward` creates an aspect that takes configs from a source class
1919-and inserts them into a target class at a specified path.
2626+## The `forward` Battery
20272121-### Example: Den `user` class works on both NixOS and nix-Darwin
2828+`den.provides.forward` creates a new class by forwarding its module contents
2929+into a target path on an existing class:
3030+3131+```nix
3232+{ host }:
3333+den.provides.forward {
3434+ each = lib.attrValues host.users;
3535+ fromClass = user: "user";
3636+ intoClass = user: host.class;
3737+ intoPath = user: [ "users" "users" user.userName ];
3838+ fromAspect = user: den.aspects.${user.aspect};
3939+}
4040+```
4141+4242+| Parameter | Description |
4343+|---|---|
4444+| `each` | List of items to forward (typically `[ user ]` or `[ true ]`) |
4545+| `fromClass` | The custom class name to read from |
4646+| `intoClass` | The target class to write into |
4747+| `intoPath` | Target attribute path in the target class |
4848+| `fromAspect` | The aspect to read the custom class from |
4949+5050+## Example: The Built-in `user` Class
22512323-Den already provides a `user` class like this example,
2424-that forwards into the OS `user.user.<userName>` submodule.
5252+The `user` class (`modules/aspects/provides/os-user.nix`) forwards OS-level
5353+user settings without requiring Home Manager:
25542655```nix
2727-userClass = { host }: { class, aspect-chain }:
2828- den._.forward {
2929- each = host.users;
3030- fromClass = _user: "user";
3131- intoClass = _user: class; # the class being resolved: nixos or darwin.
3232- intoPath = user: [ "users" "users" user.userName ];
3333- fromAspect = user: den.aspects.${user.aspect};
3434- adaptArgs = os-args: { osConfig = os-args.config; };
5656+# Instead of:
5757+den.aspects.alice.nixos = { pkgs, ... } {
5858+ users.users.alice = {
5959+ packages = [ pkgs.hello ];
6060+ extraGroups = [ "wheel" ];
3561 };
3636-3737-# this would enable it on all hosts
3838-# den.ctx.host.includes = [ userClass ];
6262+};
39634040-# settings at `user` class forward into host `users.users.<userName>`
4141-den.aspects.tux.user = { osConfig, pkgs, ... }: {
6464+# You write:
6565+den.aspects.alice.user = { pkgs, ... }: {
4266 packages = [ pkgs.hello ];
6767+ extraGroups = [ "wheel" ];
4368};
4469```
45704646-### Example: A git class that forwards to home-manager.
7171+The `user` class is automatically forwarded to `users.users.<userName>` on
7272+whatever OS class the host uses (NixOS or Darwin).
7373+7474+## Creating Your Own Class
7575+7676+Suppose you want a `container` class that forwards into
7777+`virtualisation.oci-containers.containers.<name>`:
7878+7979+```nix
8080+{ den, lib, ... }:
8181+let
8282+ fwd = { host, user }:
8383+ den.provides.forward {
8484+ each = lib.singleton user;
8585+ fromClass = _: "container";
8686+ intoClass = _: host.class;
8787+ intoPath = _: [ "virtualisation" "oci-containers" "containers" user.userName ];
8888+ fromAspect = _: den.aspects.${user.aspect};
8989+ };
9090+in {
9191+ den.ctx.user.includes = [ fwd ];
9292+}
9393+```
9494+9595+Now any user aspect can use the `container` class:
9696+9797+```nix
9898+den.aspects.alice.container = {
9999+ image = "nginx:latest";
100100+ ports = [ "8080:80" ];
101101+};
102102+```
103103+104104+## Advanced: Guards and Adapters
105105+106106+`forward` supports optional parameters for complex scenarios:
107107+108108+```nix
109109+den.provides.forward {
110110+ each = lib.singleton true;
111111+ fromClass = _: "wsl";
112112+ intoClass = _: host.class;
113113+ intoPath = _: [ "wsl" ];
114114+ fromAspect = _: lib.head aspect-chain;
115115+ # Only forward if target has wsl options
116116+ guard = { options, ... }: options ? wsl;
117117+ # Modify module args for the forwarded module
118118+ adaptArgs = args: args // { osConfig = args.config; };
119119+ # Custom module type for the forwarded submodule
120120+ adapterModule = { config._module.freeformType = lib.types.anything; };
121121+}
122122+```
123123+124124+| Parameter | Description |
125125+|---|---|
126126+| `guard` | Only forward when this predicate returns true |
127127+| `adaptArgs` | Transform module arguments before forwarding |
128128+| `adapterModule` | Custom module for the forwarded submodule type |
129129+130130+## User contributed examples
131131+132132+#### Example: A git class that forwards to home-manager.
4713348134```nix
49135gitClass =
···6515166152This will set at host: `home-manager.users.tux.programs.git.userEmail`
671536868-### Example: A `nix` class that propagates settings to NixOS and HomeManager
154154+#### Example: A `nix` class that propagates settings to NixOS and HomeManager
6915570156> Contributed by @musjj
71157···91177# included at users who can fix things with nix.
92178den.aspects.tux.includes = [ nix-allowed ];
93179```
9494-9595-## Use Cases
9696-9797-- **User environments**: Forward a `user` class into `users.users.<name>`
9898-- **nix-maid / hjem**: Built-in integrations, see [batteries](/guides/batteries/)
9999-- **Containerization**: Forward a `container` class into systemd-nspawn configs
100100-- **VM configs**: Forward a `vm` class into microvm or QEMU settings
101101-- **Guarded classes**: Forward only when target options exist (eg, Impermanence)
102102-- **Custom tools**: Forward into any Nix-configurable system (NixVim, etc.)
103103-104104-## Guarded Forward
105105-106106-Use `guard` to conditionally forward only when target options exist:
107107-108108-```nix
109109-# Custom `persys` forwards config into nixos.environment.persistance."/nix/persist/system"
110110-# only if environment.persistance option is present.
111111-persys = { class, aspect-chain }: den._.forward {
112112- each = lib.singleton true;
113113- fromClass = _: "persys";
114114- intoClass = _: class;
115115- intoPath = _: [ "environment" "persistance" "/nix/persist/system" ];
116116- fromAspect = _: lib.head aspect-chain;
117117- guard = { options, ... }@osArgs: options ? environment.persistance;
118118-};
119119-120120-den.hosts.my-laptop.includes = [ persys ];
121121-122122-# becomes nixos.environment.persistance."/nix/persist/system".hideMounts = true;
123123-den.aspects.my-laptop.persys.hideMounts = true;
124124-```
125125-126126-One cool thing about these custom classes is that aspects can simply define
127127-settings at them, without having to worry if the options they depend or
128128-some feature is enabled. The guard itself is reponsible for that check
129129-in only one place, instead of having `mkIf` in a lot of places.
130130-131131-## Creating Your Own
132132-133133-The `forward` function parameters:
134134-135135-| Parameter | Description |
136136-|-----------|-------------|
137137-| `each` | List of items to iterate over |
138138-| `fromClass` | Source class name (string) |
139139-| `intoClass` | Target class name (string) |
140140-| `intoPath` | Attribute path in target (list of strings) |
141141-| `fromAspect` | Source aspect to read from |
142142-| `guard` | Optional: `{ options, ... }@args -> bool` — forward only when true |
143143-| `adaptArgs` | Optional: args->specialArgs for the intermediate submodule |
+41-36
docs/src/content/docs/guides/debug.md
···33description: Tools and techniques for debugging Den configurations.
44---
5566-## builtins.trace
77-88-Print values during evaluation:
99-1010-```nix
1111-den.aspects.foo = { user, ... }@context:
1212- (builtins.trace context {
1313- nixos = { };
1414- });
1515-```
1616-1717-## builtins.break
1818-1919-Drop into a REPL at any evaluation point:
2020-2121-```nix
2222-den.aspects.foo = { user, ... }@context:
2323- (builtins.break context {
2424- nixos = { };
2525- });
2626-```
2727-2828-296## REPL Inspection
307318Load your flake and explore interactively:
···3714"igloo"
3815```
39164040-## Expose den for Inspection
1717+## Expose `den` for Inspection
41184219Temporarily expose the `den` attrset as a flake output:
43204421```nix
4522{ den, ... }: {
4646- flake.den = den; # remove when done
2323+ flake.den = den; # remove after debugging
4724}
4825```
4926···5330nix-repl> :lf .
5431nix-repl> den.aspects.igloo
5532nix-repl> den.hosts.x86_64-linux.igloo
3333+nix-repl> den.ctx
3434+```
3535+3636+## Trace Context
3737+3838+Print context values during evaluation:
3939+4040+```nix
4141+den.aspects.laptop.includes = [
4242+ ({ host, ... }@ctx: builtins.trace ctx {
4343+ nixos.networking.hostName = host.hostName;
4444+ })
4545+];
4646+```
4747+4848+## Break into REPL
4949+5050+Drop into a REPL at any evaluation point:
5151+5252+```nix
5353+den.aspects.laptop.includes = [
5454+ ({ host, ... }@ctx: builtins.break ctx {
5555+ nixos = { };
5656+ })
5757+];
5658```
57595860## Manually Resolve an Aspect
···6062Test how an aspect resolves for a specific class:
61636264```console
6363-nix-repl> module = den.aspects.foo.resolve { class = "nixos"; aspect-chain = []; }
6565+nix-repl> module = den.aspects.laptop.resolve { class = "nixos"; aspect-chain = []; }
6466nix-repl> config = (lib.evalModules { modules = [ module ]; }).config
6567```
66686769For parametric aspects, apply context first:
68706971```console
7070-nix-repl> aspect = den.aspects.foo { host = den.hosts.x86_64-linux.igloo; }
7272+nix-repl> aspect = den.aspects.laptop { host = den.hosts.x86_64-linux.laptop; }
7173nix-repl> module = aspect.resolve { class = "nixos"; aspect-chain = []; }
7274```
7375···75777678```console
7779nix-repl> module = den.hosts.x86_64-linux.igloo.mainModule
7878-nix-repl> config = (lib.nixosSystem { modules = [ module ]; }).config
8080+nix-repl> cfg = (lib.nixosSystem { modules = [ module ]; }).config
8181+nix-repl> cfg.networking.hostName
7982```
80838184## Common Issues
82858383-**Duplicate values in lists**: Den automatically deduplicates owned and
8484-static configs from `den.default`, but parametric functions in
8585-`den.default.includes` still run at every context stage. Use
8686-`den.lib.take.exactly` to restrict matching:
8686+**Duplicate values in lists**: Den deduplicates owned and static configs
8787+from `den.default`, but parametric functions in `den.default.includes`
8888+run at every context stage. Use `den.lib.take.exactly` to restrict:
87898890```nix
8991den.lib.take.exactly ({ host }: { nixos.x = 1; })
9092```
91939292-**Missing attribute**: The context doesn't have the expected parameter.
9393-Trace context keys to see what's available.
9494+**Missing attribute**: The context does not have the expected parameter.
9595+Trace context keys to see what is available.
9696+9797+**Wrong class**: Check that `host.class` matches what you expect.
9898+Darwin hosts have `class = "darwin"`, not `"nixos"`.
94999595-**Infinite recursion**: Aspects including each other in a cycle.
9696-Check your `includes` chains for circular dependencies.
100100+**Module not found**: Ensure the file is under `modules/` and not
101101+prefixed with `_` (excluded by import-tree).
+100-89
docs/src/content/docs/guides/declare-hosts.mdx
···11---
22title: Declare Hosts & Users
33-description: Define your infrastructure entities with Den's freeform schema.
33+description: Define machines, users, and standalone homes with den.hosts and den.homes.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">
88+99+<Aside title="Source" icon="github">
910[`modules/_types.nix`](https://github.com/vic/den/blob/main/modules/_types.nix) · Reference: [Entity Schema](/reference/schema/)
1011</Aside>
11121212-## Hosts and Users
13131414-Declare hosts with a single line per machine:
1414+## Host Declaration
1515+1616+`den.hosts` is keyed by `<system>.<name>`:
15171618```nix
1717-den.hosts.x86_64-linux.my-laptop.users.vic = { };
1818-den.hosts.aarch64-darwin.macbook.users.vic = { };
1919+{
2020+ den.hosts.x86_64-linux.laptop.users.alice = { };
2121+2222+ den.hosts.aarch64-darwin.mac = {
2323+ users.alice = { };
2424+ brew.apps = [ "iterm2" ];
2525+ };
2626+}
1927```
20282121-This creates NixOS/Darwin configurations with users sharing the same home configuration, ready for:
2929+Each host entry produces a configuration in `flake.nixosConfigurations` or
3030+`flake.darwinConfigurations` depending on its `class` (auto-detected from system).
22312323-```console
2424-nixos-rebuild switch --flake .#my-laptop
2525-darwin-rebuild switch --flake .#macbook
2626-```
3232+## Host Schema
27332828-## Customize Host Attributes
3434+Hosts have these options (all with sensible defaults):
29353030-Expand the attribute set for full control:
3636+| Option | Default | Description |
3737+|---|---|---|
3838+| `name` | attrset key | Configuration name |
3939+| `hostName` | `name` | Network hostname |
4040+| `system` | parent key | `x86_64-linux`, `aarch64-darwin`, etc. |
4141+| `class` | `"nixos"` or `"darwin"` | OS class, auto-detected from system |
4242+| `aspect` | `name` | Primary aspect name |
4343+| `instantiate` | class-dependent | `lib.nixosSystem`, `darwinSystem`, etc. |
4444+| `intoAttr` | class-dependent | Flake output path |
4545+| `users` | `{}` | User account definitions |
4646+| `*` | from `den.base.host` | Any option defined by base module |
4747+| `*` | | Any other free-form attribute |
31483232-```nix
3333-den.hosts.x86_64-linux.my-laptop = {
3434- hostName = "yavanna"; # default: my-laptop
3535- class = "nixos"; # default: guessed from platform
3636- aspect = "workstation"; # default: my-laptop
3737- intoAttr = [ "nixosConfigurations" "yavanna" ]; # default: [ "nixosConfigurations" "my-laptop" ]
3838- users.vic = {
3939- userName = "vborja"; # default: vic
4040- aspect = "oeiuwq"; # default: vic
4141- classes = ["homeManager"]; # default: ["homeManager"]
4242- };
4343-};
4444-```
4949+## User Declaration
45504646-## Standalone Home-Manager
4747-4848-For Home-Manager without a host OS:
5151+Users are declared under a host:
49525053```nix
5151-den.homes.aarch64-darwin.vic = { };
5454+den.hosts.x86_64-linux.laptop = {
5555+ users.alice = { };
5656+ users.bob.classes = [ "homeManager" "hjem" ];
5757+};
5258```
53595454-Build with:
6060+Each user has:
55615656-```console
5757-home-manager switch --flake .#vic
5858-```
6262+| Option | Default | Description |
6363+|---|---|---|
6464+| `name` | attrset key | User configuration name |
6565+| `userName` | `name` | System account name |
6666+| `aspect` | `name` | Primary aspect name |
6767+| `classes` | `[ "homeManager" ]` | Nix classes this user participates in |
6868+| `*` | from `den.base.user` | Any option defined by base module |
6969+| `*` | | Any other free-form attribute |
59706060-## Multiple Hosts, Shared Users
7171+## Standalone Homes
61726262-The same user aspect applies to every host it appears on:
7373+For systems without root access or for home-manager-only setups:
63746464-```nix "vic"
6565-den.hosts.x86_64-linux.desktop.users.vic = { };
6666-den.hosts.x86_64-linux.server.users.vic = { };
6767-den.hosts.aarch64-darwin.mac.users.vic = { };
7575+```nix
7676+{
7777+ den.homes.x86_64-linux.alice = { };
7878+ den.homes.aarch64-darwin.alice = { };
7979+}
6880```
69817070-All three machines share same `den.aspects.vic` user aspect.
8282+Standalone homes produce `flake.homeConfigurations.<name>`.
71837272-## Freeform Attributes
8484+Home schema:
73857474-Hosts and users accept arbitrary attributes. Use them for metadata
7575-that aspects can inspect:
8686+| Option | Default | Description |
8787+|---|---|---|
8888+| `name` | attrset key | Home configuration name |
8989+| `userName` | `name` | User account name |
9090+| `system` | parent key | Platform system |
9191+| `class` | `"homeManager"` | Home class |
9292+| `aspect` | `name` | Primary aspect name |
9393+| `pkgs` | `inputs.nixpkgs.legacyPackages.${system}` | nixpkgs instance |
9494+| `instantiate` | `inputs.home-manager.lib.homeManagerConfiguration` | Builder function |
9595+| `*` | from `den.base.host` | Any option defined by base module |
9696+| `*` | | Any other free-form attribute |
76977777-```nix
7878-den.hosts.x86_64-linux.my-laptop = {
7979- isWarm = true; # custom attribute
8080- location = "studio"; # custom attribute
8181- users.vic = { };
8282-};
8383-```
9898+## Base Modules
84998585-Then in an aspect: `{ host, ... }: if host.isWarm then ...`
100100+`den.base.{host,user,home,conf}` provides shared configuration applied to all entities of each kind.
861018787-## Base Modules
8888-8989-Add type-checked options to all hosts, users, or homes:
102102+Some batteries also extend base modules [see `hjem-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/hjem/hjem-os.nix#L50) which defines `hjem.module` option.
9010391104```nix
9292-den.base.host = { host, lib, ... }: {
9393- options.vpn-alias = lib.mkOption { default = host.name; };
9494-};
105105+{
106106+ # Can be used to config each host
107107+ den.base.host.home-manager.enable = true;
951089696-den.base.user = { user, lib, ... }: {
9797- options.main-group = lib.mkOption { default = user.name; };
9898-};
109109+ # Can be used to add schema options with defaults
110110+ den.base.user = { user, lib, ... }: {
111111+ options.groupName = lib.mkOption { default = user.userName; };
112112+ };
99113100100-den.base.conf = { lib, ... }: {
101101- options.org = lib.mkOption { default = "acme"; };
102102-};
114114+ # Applied to every host and user and home.
115115+ den.base.conf = {
116116+ options.copyright = lib.mkOption { default = "Copy-Left"; };
117117+ };
118118+}
103119```
104120105105-`den.base.conf` applies to hosts, users, **and** homes.
106106-107107-## Custom Instantiation
121121+## Aspect Association
108122109109-Different `nixpkgs` channels per host:
123123+Each host and user has an `aspect` attribute name (defaults to the config `name`).
124124+Den looks up `den.aspects.${aspect}` to find the configuration for that entity:
110125111126```nix
112112-den.hosts.x86_64-linux.stable-server = {
113113- instantiate = inputs.nixpkgs-stable.lib.nixosSystem;
114114- users.admin = { };
115115-};
127127+{
128128+ den.hosts.x86_64-linux.laptop.users.alice = { };
129129+ # Den uses den.aspects.laptop for the host
130130+ # Den uses den.aspects.alice for the user
131131+}
116132```
117133118118-Override where configurations are placed:
134134+## Freeform Schema
135135+136136+Host and user types use `freeformType`, so you can add arbitrary attributes:
119137120138```nix
121121-den.hosts.x86_64-linux.wsl-box = {
122122- intoAttr = [ "wslConfigurations" "my-wsl" ];
123123- wsl.enable = true;
124124- users.wsl = { };
139139+den.hosts.x86_64-linux.laptop = {
140140+ users.alice = { };
141141+ gpu = "nvidia"; # custom attribute, accessible in aspects via host.gpu
125142};
126143```
127144128128-Custom nixpkgs instance, home-manager osConfig and output attribute:
145145+Access custom attributes in aspects:
129146130147```nix
131131-den.homes.x86_64-linux.tux-at-igloo = {
132132- aspect = "tux"; # Use den.aspects.tux for configuration
133133- intoAttr = [ "homeConfigurations" "tux@igloo" ];
134134- instantiate = { pkgs, modules }:
135135- inputs.home-manager-stable.lib.homeManagerConfiguration {
136136- inherit modules;
137137- pkgs = inputs.self.nixpkgs-stable.legacyPackages.x86_64-linux;
138138- extraSpecialArgs.osConfig = inputs.self.nixosConfigurations.igloo.config;
139139- };
140140-};
148148+den.aspects.laptop.includes = [
149149+ ({ host, ... }: lib.optionalAttrs (host ? gpu) {
150150+ nixos.hardware.nvidia.enable = true;
151151+ })
152152+];
141153```
142142-
+106-73
docs/src/content/docs/guides/home-manager.mdx
···11---
22-title: Homes Integration (hm / hjem / nix-maid, etc)
33-description: Integrate Home-Manager into NixOS/Darwin hosts or use standalone.
22+title: Homes Integration
33+description: Home Manager, hjem, and nix-maid setup with Den.
44---
55-6576import { Aside } from '@astrojs/starlight/components';
8799-<Aside title="Use the Source, Luke" icon="github">
88+99+<Aside title="Source" icon="github">
1010[`hm-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-os.nix) · [`hm-integration.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-integration.nix)
11111212[`hjem-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/hjem/hjem-os.nix) · [`hjem-integration.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/hjem/hjem-integration.nix)
···15151616</Aside>
17171818-Den supports different Nix libraries for managing user home files.
1919-All of them have Nix classes implemented using `den.provides.forward`
2020-and you can create custom home-like classes following the same pattern.
1818+## Home Manager
1919+2020+### Enabling on Homes
21212222-- `user` class. NixOS and nix-Darwin builtin `users.users.<userName>` submodule.
2323-- `homeManager` class. Enabled via `host.home-manager.enable`
2424-- `hjem` class. Enabled via `host.hjem.enable`
2525-- `maid` class. Enabled via `host.nix-maid.enable`.
2222+All Home integrations are opt-in and must be enabled explicitly.
26232727-Class names follow the Nix class used by the module system of these projects
2828-not the project name, e.g: [`homeManager`](https://github.com/nix-community/home-manager/blob/f140aa04d7d14f8a50ab27f3691b5766b17ae961/modules/default.nix#L32).
2424+```nix
2525+# Per user
2626+den.hosts.x86_64-linux.igloo.users.tux.classes = [ "homeManager" "hjem" ];
29273030-Except for the `user` class which is always enabled, other home classes
3131-are opt-in features, enabled via the host configuration:
2828+# Globally for all users
2929+den.base.user.classes = [ "homeManager" ];
3030+```
32313333-```nix
3434-# per host:
3535-den.hosts.x86_64-linux.igloo.home-manager = {
3636- enable = true;
3232+Home integration contexts, like `den.ctx.hm-host` only activate when
3333+at least one host user has `homeManager` in their `classes`.
3434+When true, the integration imports the HomeManager OS module,
3535+and forwards each user's `homeManager` class into `home-manager.users.<userName>`.
37363838- # optionally with custom stable/unstable HM input
3939- module = inputs.home-manager-unstable.nixosModules.default;
4040-};
3737+Same details regarding `home-manager` apply to other home types like `hjem` and `maid`.
41384242-# Globally (or conditionally on host)
4343-den.base.host = { host, ... }: {
4444- home-manager.enable = host.name == "igloo";
4545-};
3939+### Requirements
4040+4141+- `inputs.home-manager` must exist in your flake inputs or have custom `host.home-manager.module`.
4242+- At least one user must have `homeManager` in their `classes`.
4343+4444+### How it Works
4545+4646+```mermaid
4747+flowchart TD
4848+ host["den.ctx.host"] -->|"into.hm-host"| hmhost["den.ctx.hm-host"]
4949+ hmhost -->|"imports HM module"| mod["home-manager.nixosModules"]
5050+ hmhost -->|"into.hm-user (per user)"| hmuser["den.ctx.hm-user"]
5151+ hmuser -->|"forward homeManager class"| target["home-manager.users.alice"]
4652```
47534848-## Multi Home support.
5454+1. `hm-os.nix` detects hosts with HM-enabled users and supported OS class.
5555+2. It produces `den.ctx.hm-host`, which imports the HM OS-level module.
5656+3. `hm-integration.nix` creates `den.ctx.hm-user` per HM user, forwarding
5757+ the `homeManager` class into `home-manager.users.<userName>`.
5858+5959+### Configuring Home Manager
6060+6161+```nix
6262+den.aspects.alice = {
6363+ homeManager = { pkgs, ... }: {
6464+ home.packages = [ pkgs.htop ];
6565+ programs.git.enable = true;
6666+ };
6767+};
6868+```
49695050-User can choose to manage some files with home-manager instead or in
5151-addition to nix-maid and hjem. For example, when migrating away from home-manager into other libraries, you might want to keep your stuff working as you move progressively.
7070+The `homeManager` class contents are forwarded to the OS-level
7171+`home-manager.users.alice` automatically.
52725353-Users define a `<user>.classes = [ "homeManager" ]` (default) where they
5454-explicitly enable the different home libraries they use.
7373+### Custom HM Module
55745656-## Host-Managed Users
7575+Override the HM module per host if needed:
57765877```nix
5959-den.hosts.x86_64-linux.igloo.users.tux = { };
7878+den.hosts.x86_64-linux.laptop = {
7979+ users.vic.classes = [ "home-manager" ];
8080+ home-manager.module = inputs.home-manager-unstable.nixosModules.home-manager;
8181+};
8282+```
60836161-# host meta configuration (capabilities), HM always enabled.
6262-den.base.host.home-manager.enable = lib.mkDefault true;
8484+## Standalone Homes
63856464-# homeManager default settings
6565-den.default.homeManager.home.stateVersion = lib.mkDefault "25.11";
8686+For machines without root access:
66876767-# NixOS/Nix-Darwin user class.
6868-den.aspects.tux.user.description = "The Penguin";
8888+```nix
8989+den.homes.x86_64-linux.alice = { };
9090+```
69917070-# homeManager class
7171-den.aspects.tux.homeManager.programs.vim.enable = true;
7272-```
9292+This produces `flake.homeConfigurations.alice`, built with
9393+`inputs.home-manager.lib.homeManagerConfiguration`.
73947474-User description forwarded into `igloo.users.users.tux.description`.
7575-The vim config is forwarded into `igloo.home-manager.users.tux`.
9595+## hjem
76967777-## Standalone Home-Manager
9797+[hjem](https://github.com/feel-co/hjem) is an alternative, lightweight home environment manager.
78987979-For Home-Manager without an OS host:
9999+### Enabling
8010081101```nix
8282-den.homes.aarch64-darwin.vic = { };
8383-den.default.homeManager.home.stateVersion = "25.11";
8484-den.aspects.vic.homeManager.programs.fish.enable = true;
102102+# Per host
103103+den.hosts.x86_64-linux.laptop = {
104104+ users.alice.classes = [ "hjem" ];
105105+};
106106+107107+# On all hosts
108108+den.base.host.hjem.enable = true;
85109```
861108787-Build with `home-manager switch --flake .#vic`.
111111+### Requirements
881128989-## Context types for Home enabled hosts.
113113+- `inputs.hjem` must exist.
114114+- Users must have `hjem` in their `classes`.
901159191-Each Home integration in Den defines a context
9292-transformation from `den.ctx.host -> den.ctx.${name}-host`.
116116+### Using
117117+118118+```nix
119119+den.aspects.alice.hjem = { };
120120+```
931219494-For example, `den.ctx.hm-host` is the aspect for Home-Manager enabled hosts.
9595-Similarly `den.ctx.maid-host` and `den.ctx.hjem-host`.
122122+## nix-maid
961239797-## useGlobalPkgs at Home Manager enabled hosts
124124+[nix-maid](https://github.com/nix-maid) is another user-environment manager for NixOS.
981259999-Configure a NixOS option only for Home-Manager hosts
100100-so they can use the host's nixpkgs:
126126+### Enabling
101127102128```nix
103103-den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true;
129129+den.hosts.x86_64-linux.laptop = {
130130+ users.alice.classes = [ "maid" ];
131131+};
104132```
105133106106-This only activates for hosts that actually have Home-Manager users.
107107-Hosts without Home-Manager users are unaffected.
134134+### Requirements
108135109109-## Standalone with osConfig
136136+- `inputs.nix-maid` must exist.
137137+- Host class must be `"nixos"`.
138138+- Users must have `maid` in their `classes`.
110139111111-Access NixOS config from standalone Home-Manager:
140140+### Using
112141113142```nix
114114-den.homes.x86_64-linux.pingu = {
115115- instantiate = { pkgs, modules }:
116116- inputs.home-manager.lib.homeManagerConfiguration {
117117- inherit pkgs modules;
118118- extraSpecialArgs.osConfig =
119119- config.flake.nixosConfigurations.igloo.config;
120120- };
143143+den.aspects.alice.maid = {
144144+ # nix-maid configuration
121145};
146146+```
122147123123-den.aspects.pingu.homeManager = { osConfig, ... }: {
124124- programs.emacs.enable = osConfig.programs.vim.enable;
148148+## Multiple User Environments
149149+150150+A user can participate in multiple environments:
151151+152152+```nix
153153+den.hosts.x86_64-linux.laptop = {
154154+ users.alice.classes = [ "homeManager" "hjem" ];
155155+ home-manager.enable = true;
156156+ hjem.enable = true;
125157};
126158```
127159128128-160160+Both `homeManager` and `hjem` configurations from `den.aspects.alice` will
161161+be forwarded to their respective targets.
-101
docs/src/content/docs/guides/migrate.md
···11----
22-title: Migrate to Den
33-description: Incrementally adopt Den in existing Nix configurations.
44----
55-66-## Start Small
77-88-Den can be adopted incrementally. You don't need to rewrite your entire
99-configuration — start by adding one Den-managed host alongside your
1010-existing setup.
1111-1212-## From Flake-Parts Dendritic
1313-1414-If you're already using `flake.modules`, migration is direct:
1515-1616-```nix
1717-# Before (flake-parts dendritic)
1818-flake.modules.nixos.desktop = { ... };
1919-flake.modules.homeManager.games = { ... };
2020-2121-# After (Den)
2222-den.aspects.desktop.nixos = { ... };
2323-den.aspects.games.homeManager = { ... };
2424-```
2525-2626-## Import Existing Modules
2727-2828-You don't need to convert all modules. Import them directly:
2929-3030-```nix
3131-{ inputs, ... }: {
3232- den.aspects.desktop.nixos.imports = [
3333- inputs.disko.nixosModules.disko
3434- inputs.self.modules.nixos.desktop # existing module
3535- ];
3636-}
3737-```
3838-3939-## Mix Den with Existing nixosSystem
4040-4141-Use `mainModule` to integrate Den into existing configurations:
4242-4343-```nix
4444-let
4545- denCfg = (lib.evalModules {
4646- modules = [ (import-tree ./modules) ];
4747- specialArgs = { inherit inputs; };
4848- }).config;
4949-in
5050- lib.nixosSystem {
5151- modules = [
5252- ./hardware-configuration.nix # your existing modules
5353- ./networking.nix
5454- denCfg.den.hosts.x86_64-linux.igloo.mainModule # Den modules
5555- ];
5656- }
5757-```
5858-5959-## Use import-tree for Gradual Migration
6060-6161-The [`import-tree`](/reference/batteries/#import-tree) battery auto-imports files by class directory:
6262-6363-```
6464-non-dendritic/
6565- hosts/
6666- my-laptop/
6767- _nixos/
6868- hardware.nix
6969- networking.nix
7070- _homeManager/
7171- shell.nix
7272-```
7373-7474-```nix
7575-den.ctx.host.includes = [
7676- (den._.import-tree._.host ./non-dendritic/hosts)
7777-];
7878-```
7979-8080-Files in `_nixos/` import as NixOS modules, `_homeManager/` as HM modules.
8181-8282-## Access Den Configurations Directly
8383-8484-Den's outputs are standard Nix configurations:
8585-8686-```nix
8787-# These are normal nixosSystem / homeManagerConfiguration results
8888-config.flake.nixosConfigurations.igloo
8989-config.flake.homeConfigurations.tux
9090-```
9191-9292-Expose them directly in your flake outputs alongside existing ones.
9393-9494-## Recommended Migration Path
9595-9696-1. Add Den inputs to your flake
9797-2. Create a `modules/` directory with `hosts.nix`
9898-3. Add one host with `den.hosts`
9999-4. Move one aspect at a time from existing modules
100100-5. Use `import-tree` for files you haven't converted yet
101101-6. Gradually expand Den-managed aspects
+119
docs/src/content/docs/guides/migrate.mdx
···11+---
22+title: Migrate to Den
33+description: Incremental migration from existing NixOS/Darwin setups.
44+---
55+66+import { Steps } from '@astrojs/starlight/components';
77+88+## Strategy
99+1010+Migration to Den is incremental. You do not need to rewrite everything at once.
1111+1212+<Steps>
1313+1414+1. Add Den as Input
1515+1616+ Add Den to your flake and import the flake module:
1717+1818+ ```nix
1919+ {
2020+ inputs.den.url = "github:vic/den";
2121+ inputs.import-tree.url = "github:vic/import-tree";
2222+ inputs.flake-aspects.url = "github:vic/flake-aspects";
2323+2424+ outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; }
2525+ (inputs.import-tree ./modules);
2626+ }
2727+ ```
2828+2929+ ```nix
3030+ # modules/den.nix
3131+ { inputs, ... }: {
3232+ imports = [ inputs.den.flakeModule ];
3333+ }
3434+ ```
3535+3636+2. Declare Hosts
3737+3838+ Move your host declarations into `den.hosts`:
3939+4040+ ```nix
4141+ {
4242+ den.hosts.x86_64-linux.laptop.users.alice = { };
4343+ }
4444+ ```
4545+4646+3. Import Existing Modules
4747+4848+ Use `den.provides.import-tree` to load your existing non-dendritic modules:
4949+5050+ ```nix
5151+ # modules/legacy.nix
5252+ { den, ... }: {
5353+ den.ctx.host.includes = [
5454+ (den.provides.import-tree._.host ./hosts)
5555+ ];
5656+ den.ctx.user.includes = [
5757+ (den.provides.import-tree._.user ./users)
5858+ ];
5959+ }
6060+ ```
6161+6262+ With this directory structure:
6363+6464+ ```
6565+ hosts/
6666+ laptop/
6767+ _nixos/
6868+ hardware.nix
6969+ networking.nix
7070+ _homeManager/
7171+ shell.nix
7272+ users/
7373+ alice/
7474+ _homeManager/
7575+ git.nix
7676+ _nixos/
7777+ groups.nix
7878+ ```
7979+8080+ Files under `_nixos/` are imported as NixOS modules, `_homeManager/` as
8181+ Home Manager modules, etc. This requires `inputs.import-tree`.
8282+8383+4. Extract Aspects
8484+8585+ Gradually extract features from your legacy modules into Den aspects:
8686+8787+ ```nix
8888+ # modules/dev-tools.nix
8989+ {
9090+ den.aspects.dev-tools = {
9191+ nixos = { pkgs, ... }: {
9292+ environment.systemPackages = with pkgs; [ git vim tmux ];
9393+ };
9494+ homeManager.programs.git.enable = true;
9595+ };
9696+ }
9797+ ```
9898+9999+ ```nix
100100+ # modules/laptop.nix
101101+ { den, ... }: {
102102+ den.aspects.laptop.includes = [ den.aspects.dev-tools ];
103103+ }
104104+ ```
105105+106106+5. Remove Legacy
107107+108108+ As aspects replace legacy modules, remove the corresponding files from
109109+ `hosts/` and `users/`. Eventually remove `den.provides.import-tree` usage.
110110+111111+</Steps>
112112+113113+## Tips
114114+115115+- **Start with one host.** Migrate a single machine first to learn the pattern.
116116+- **Keep legacy working.** `import-tree` loads your existing files alongside
117117+ Den aspects -- they coexist without conflicts.
118118+- **Use batteries.** Replace manual user/shell/HM setup with `den.provides.*`.
119119+- **Test with VM.** Use `nix run .#vm` to validate changes before applying to hardware.
+71-71
docs/src/content/docs/guides/namespaces.mdx
···11---
22title: Share with Namespaces
33-description: Share and consume aspect libraries across repositories.
33+description: Publish and consume aspect libraries across flakes.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">[`nix/namespace.nix`](https://github.com/vic/den/blob/main/nix/namespace.nix)</Aside>
99-1010-## What Are Namespaces?
1111-1212-Namespaces let you organize aspects into named collections that can be
1313-shared across flakes and consumed by others.
88+<Aside title="Source" icon="github">
99+[`nix/namespace.nix`](https://github.com/vic/den/blob/main/nix/namespace.nix)
1010+</Aside>
14111515-This is unlike your `den.aspects.<name>` namespace which is private,
1616-because you wont likely want to share your user/host configurations.
1212+## What are Namespaces
17131818-Namespaces are for re-usable aspects you are willing to share with others.
1414+A **namespace** creates a scoped aspect library under `den.ful.<name>`.
1515+Namespaces can be:
1616+- **Local**: defined in your flake, consumed internally.
1717+- **Exported**: exposed via `flake.denful.<name>` for other flakes to consume.
1818+- **Imported**: merged from upstream flakes into your local `den.ful`.
19192020-## Define a Local Namespace
2020+## Creating a Namespace
21212222```nix
2323-{ inputs, ... }: {
2424- imports = [ (inputs.den.namespace "ns" false) ];
2525- ns.tools.nixos.programs.vim.enable = true;
2323+# modules/namespace.nix
2424+{ inputs, den, ... }: {
2525+ # Create "my" namespace (not exported to flake outputs)
2626+ imports = [ (inputs.den.namespace "my" false) ];
2727+2828+ # Or create and export "eg" namespace
2929+ imports = [ (inputs.den.namespace "eg" true) ];
2630}
2731```
28322929-The first argument is the aspect namespace name.
3030-It will be provided as module argument: `{ lib, ns, ... }`
3131-so you can access aspects from the namespace.
3333+This creates:
3434+- `den.ful.eg` -- the namespace attrset (aspects type).
3535+- `eg` -- a module argument alias to `den.ful.eg`.
3636+- `flake.denful.eg` -- flake output (if exported).
32373333-The second argument controls output:
3434-- `false` — local only, not exposed as flake output
3535-- `true` — exposed at `flake.denful.ns`
3636-- A list of sources — merge from external inputs
3737-3838-## Consume Remote Namespaces
3838+## Populating a Namespace
39394040-Import aspects from another flake's `flake.denful.provider` output:
4040+Define aspects under the namespace using any module:
41414242```nix
4343-{ inputs, ... }: {
4444- imports = [
4545- (inputs.den.namespace "provider" [ true inputs.other-flake ])
4646- ];
4747-4848- den.aspects.igloo.includes = [ provider.tools._.editors ];
4343+# modules/aspects/vim.nix
4444+{
4545+ eg.vim = {
4646+ homeManager.programs.vim.enable = true;
4747+ };
4948}
5049```
51505252-Multiple sources are merged by the module system.
5353-5454-## Nested Provides in Namespaces
5555-5656-Namespaces support the full aspect tree with `provides`:
5757-5851```nix
5959-ns.root.provides.branch.provides.leaf.nixos.truth = true;
6060-# access via:
6161-ns.root._.branch._.leaf
5252+# modules/aspects/desktop.nix
5353+{ eg, ... }: {
5454+ eg.desktop = {
5555+ includes = [ eg.vim ];
5656+ nixos.services.xserver.enable = true;
5757+ };
5858+}
6259```
63606464-## Expose as Flake Output
6161+## Using Namespaced Aspects
65626666-When the second argument is `true` (or a list containing `true`),
6767-the namespace appears at `config.flake.denful.<name>`:
6363+Reference them by their namespace:
68646965```nix
7070-imports = [ (inputs.den.namespace "ns" true) ];
7171-ns.foo.nixos.truth = true;
7272-# available at config.flake.denful.ns
6666+{ eg, ... }: {
6767+ den.aspects.laptop.includes = [
6868+ eg.desktop
6969+ eg.vim
7070+ ];
7171+}
7372```
74737575-Other flakes can then consume it:
7474+## Importing from Upstream
7575+7676+Merge aspects from other flakes:
76777778```nix
7878-inputs.your-flake.denful.ns
7979+# modules/namespace.nix
8080+{ inputs, ... }: {
8181+ # Import "shared" namespace from upstream, merging with local definitions
8282+ imports = [ (inputs.den.namespace "shared" [ inputs.team-config ]) ];
8383+}
7984```
80858181-## Merge Multiple Sources
8686+The namespace function accepts:
8787+- A **boolean** (`true`/`false`) for local/exported namespaces.
8888+- A **list of sources** to merge from upstream flakes.
8989+ Each source's `flake.denful.<name>` is merged into `den.ful.<name>`.
82908383-Combine local, remote, and output in one namespace:
9191+## Enabling Angle Brackets
9292+9393+When using namespaces, enable angle bracket syntax for terser references:
84948595```nix
8686-imports = [
8787- (inputs.den.namespace "ns" [
8888- inputs.sourceA
8989- inputs.sourceB
9090- true # also expose as output
9191- ])
9292-];
9393-9494-ns.gear.nixos.data = [ "local" ];
9595-# merges with sourceA and sourceB's denful.ns.gear
9696+{ den, ... }: {
9797+ _module.args.__findFile = den.lib.__findFile;
9898+}
9699```
971009898-## Use with Angle Brackets
9999-100100-When `__findFile` is in scope, namespace aspects are accessible via
101101-angle brackets:
101101+Then reference deep aspects with `<namespace/path>`:
102102103103```nix
104104-{ __findFile, ... }: {
105105- _module.args.__findFile = den.lib.__findFile;
106106-107107- den.aspects.igloo.includes = [ <ns/tools> ];
108108-}
104104+den.aspects.laptop.includes = [ <eg/desktop> ];
109105```
110106111111-## Real-World: denful
107107+## Architecture
112108113113-[denful](https://github.com/vic/denful) is a (WIP) community aspect distribution
114114-built on Den namespaces — a lazyvim-like approach to Nix configurations.
109109+```mermaid
110110+flowchart LR
111111+ upstream["upstream flake"] -->|"flake.denful.shared"| merge["den.ful.shared"]
112112+ local["local modules"] -->|"shared.vim = ..."| merge
113113+ merge --> consumer["den.aspects.*.includes"]
114114+```
+83-106
docs/src/content/docs/index.mdx
···11---
22title: Context-aware Dendritic Nix
33-description: Context-driven, aspect-oriented, cross-class Nix configurations.
33+description: Aspect-oriented, context-driven Nix configurations for NixOS, nix-darwin, and home-manager.
44template: splash
55hero:
66- tagline: Context-driven, aspect-oriented, cross-class Nix configurations
66+ tagline: Aspect-oriented, context-driven Nix configurations
77 image:
88 html: |
99 <img width="400" height="400" src="https://github.com/user-attachments/assets/af9c9bca-ab8b-4682-8678-31a70d510bbb" />
···2929 variant: minimal
3030---
31313232-import { LinkButton, Card, CardGrid } from '@astrojs/starlight/components';
3232+import { Steps, LinkButton, Card, CardGrid } from '@astrojs/starlight/components';
33333434## Testimonials
35353636> Den takes the Dendritic pattern to a whole new level, and I cannot imagine going back.
3737-> @adda - Very early Den adopter after using Dendritic flake-parts and Unify.
3737+> -- `@adda` - Very early Den adopter after using Dendritic flake-parts and Unify.
3838+38393940> I'm super impressed with den so far, I'm excited to try out some new patterns that Unify couldn't easily do.
4040-> @quasigod - Unify dendritic-framework author on adopting Den. [\[repo\]](https://tangled.org/quasigod.xyz/nixconfig)
4141+> -- `@quasigod` - Unify dendritic-framework author on adopting Den. [\[repo\]](https://tangled.org/quasigod.xyz/nixconfig)
4242+41434244> Massive work you did here!
4343-> @drupol - Author of "Flipping the Configuration Matrix" Dendritic blog post. [\[repo\]](https://github.com/drupol/infra/tree/push-woqtkxkpstro)
4545+> -- `@drupol` - Author of "Flipping the Configuration Matrix" Dendritic blog post. [\[repo\]](https://github.com/drupol/infra/tree/push-woqtkxkpstro)
4646+44474548> Thanks for the awesome library and the support for non-flakes... it’s positively brilliant!. I really hope this gets wider adoption.
4646-> @vczf - At den matrix channel.
4949+> -- `@vczf` - At den matrix channel.
475048514952## What is Den
50535151-At its core, Den is a **library** for activating Nix-modules configuration aspects via context transformations.
5454+5555+At its core, Den is a **library** for activating Nix configuration aspects via context transformations.
52565357On top of the library, Den provides a **framework** for the common case
5454-of NixOS/Darwin/Home-Manager.
5858+of NixOS/nix-Darwin/Home-Manager.
55595660Den embraces your Nix choices and does not impose itself. All parts of Den are optional and replaceable.
57615858-## Two Core Principles
5959-6060-Den builds on [flake-aspects](https://github.com/vic/flake-aspects)' **parametric aspects**,
6161-and provides a declarative **context-pipeline** as cutting-points for these aspects.
6262-6363-<CardGrid>
6464- <Card title="Context Transformation" icon="random">
6565- Data flows through a declarative [pipeline](/explanation/context-pipeline/) of
6666- [contexts](/explanation/context-system/). Each stage enriches the context — from
6767- host definitions through user enumeration to domain-specific requirements.
6868- </Card>
6969- <Card title="Context-Aware Aspects" icon="puzzle">
7070- [Aspects](/explanation/aspects/) are composable bundles of cross-class Nix configs.
7171- They inspect context to produce conditional, [parametric](/explanation/parametric/)
7272- configurations — and are activated by a matching context.
7373- </Card>
7474-</CardGrid>
7575-7676-7777-### 1. Context Transformation
6262+## How Den works
78637979-Data flows through a declarative pipeline. You declare the entities (data) that exists in your universe, Den transforms them into progressively richer contexts to which aspects are attached, providing configuration for each context:
6464+Den uses a Dendritic Nix configuration model. Features are organized using [flake-aspects](https://github.com/vic/flake-aspects) -- composable attrsets with per-class configurations and
6565+a dependency graph (`provides/includes`). Den's **context pipeline** determines which aspects apply to which targets collecting the configurations they contribute to each context transformation stage, ultimately producing a single, unified system configuration.
80666767+<div style="display: grid; grid-template-columns: 1fr 1fr; grid-gap: 20px;">
6868+<div>
8169```mermaid
8270graph LR
8371 H["den.ctx.host {host}"] --> U["den.ctx.user {host, user} (for each user)"]
8472 U --> HM["aspect provides homeManager/hjeim class"]
8573 H --> OS["aspect provides nixos/darwin class"]
8674```
8787-8888-Each stage of the pipeline is a [**context type**](/explanation/context-system/) defined in `den.ctx`. Context types
8989-declare how to find [aspects](/explanation/aspects/), how to transform into other contexts, and which
9090-[parametric](/explanation/parametric/) includes to activate.
9191-9292-### 2. Context-Aware Aspects
9393-9494-Aspects are composable bundles of multi-class Nix modules that inspect their context parameters to decide
9595-what to produce. A function taking `{ host, ... }` only runs when a host context
9696-exists. A function taking `{ host, user, ... }` runs once per user on each host.
9797-9898-Functions that require parameters not present in the current context are
9999-not included. Den introduces pattern matching on context shape.
100100-101101-102102-103103-## Den as a Library
104104-105105-```nix
106106-# Use Den API -- Build the dependencies graph for NixOS hosts
107107-aspect = den.ctx.host { host = den.hosts.x86_64-linux.igloo; };
108108-109109-# Use flake-aspects API -- We enter the NixOS domain by resolving for the "nixos" class.
110110-nixosModule = aspect.resolve { class = "nixos"; };
111111-112112-# Use NixOS API -- Instantiate using nixosSystem with the resolved module.
113113-nixosConfigurations.igloo = lib.nixosSystem { modules = [ nixosModule ]; };
7575+</div>
7676+<div>
7777+```mermaid
7878+flowchart BT
7979+ subgraph "**den.aspects.dev-tools**"
8080+ n["nixos"]
8181+ h["homeManager"]
8282+ d["darwin"]
8383+ end
8484+ n --> server["server (NixOS)"]
8585+ n --> laptop["laptop (NixOS)"]
8686+ d --> mac["mac (Darwin)"]
8787+ h --> laptop
8888+ h --> mac
8989+ h --> standalone["alice (standalone HM)"]
11490```
9191+</div>
9292+</div>
11593116116-This same pattern works for any class — create your own context transformation graphs and replace `"nixos"` with
117117-`"darwin"`, `"systemManager"`, `"terranix"` or any custom class name.
118949595+## Den as a *library*
1199612097<CardGrid>
121121- <Card title="Any Nix Class" icon="nix">
122122- NixOS, Darwin, Home-Manager, Terraform, NixVim — Den
123123- works with [anything configurable through Nix](/explanation/library-vs-framework/).
9898+ <Card title="Any Nix Configuration Class" icon="puzzle">
9999+ NixOS, Darwin, system-manager, Terraform,
100100+ or [custom classes](/guides/custom-classes/)
101101+102102+ Den works with anything configurable through Nix modules and any
103103+ topology you can describe with Nix data and function transformations.
124104 </Card>
125105 <Card title="Context Pipeline" icon="right-arrow">
126126- Define custom [context types](/reference/ctx/) with `den.ctx`. Declarative
127127- [transformations](/explanation/context-pipeline/) propagate data through your config graph.
106106+ Declarative [context types](/reference/ctx/) with `den.ctx`.
107107+108108+ Each context stage can [transform](/explanation/context-pipeline/) into other contexts,
109109+ using a type-driven design, ensuring only valid contexts exist via
110110+ principles like [parse don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)
128111 </Card>
129112 <Card title="No Lock-in" icon="open-book">
130130- Works [with flakes, without flakes](/guides/no-flakes/), with flake-parts, or
131131- standalone. Den plays well with your existing Nix choices. And integrates with your existing infra.
113113+ Works with flake-parts, without flake-parts, or [without flakes at all](/tutorials/noflake/).
114114+115115+ Den never uses `lib.evalModules` directly, but provides APIs and modules for use with your favorite Nix module system.
132116 </Card>
133117 <Card title="Sharable Aspects" icon="star">
134134- Re-usablity is one of the goals of Den. Allowing people to create truly-generic configuration modules.
135135- [Namespaces](/guides/namespaces/) let you publish and consume aspect libraries
136136- across repositories and flakes.
118118+ [Namespaces](/guides/namespaces/) let you publish and consume aspect
119119+ libraries across flakes or non-flakes.
120120+121121+ Den is social, trying to bring re-usability across stable and unstable Nix boundaries.
122122+ No Flakes mandated.
137123 </Card>
138124</CardGrid>
139125140140-## Den as a Framework
126126+The following three lines is how a NixOS configuration is built.
127127+The very same process can be used for other Nix configuration domains outside
128128+of NixOS.
141129142130```nix
143143-# use in flake-parts or any lib.evalModules where den.nixModule is imported
144144-{ den, ... }: {
131131+# Use Den API -- Context transformations happen here, nothing is configured yet.
132132+aspect = den.ctx.host { host = den.hosts.x86_64-linux.igloo; };
145133146146- # declare your hosts, users and homes
147147- den.x86_64-linux.hosts.igloo.users.tux = { };
134134+# Use flake-aspects API -- We enter the NixOS domain by resolving for the "nixos" class.
135135+nixosModule = aspect.resolve { class = "nixos"; };
148136149149- # attach configurations via aspects
150150- den.igloo.nixos.networking.hostName = "warm-home";
137137+# Use NixOS API -- Instantiate using nixosSystem with the resolved module.
138138+nixosConfigurations.igloo = lib.nixosSystem { modules = [ nixosModule ]; };
139139+```
151140152152- # user aspects can configure its home and host environment
153153- den.tux.homeManager = { pkgs, ... }: { home.packages = [ pkgs.cowsay ]; };
154154- den.tux.nixos.users.users.tux.desscription = "cute pinguin";
155141156156- # include other re-usable aspects of yours or den batteries
157157- den.tux.includes = [ (den.provides.user-shell "fish") ];
142142+## Den as a _framework_.
158143159159- # generic config for all hosts, users, homes.
160160- den.default.nixos.system.stateVersion = "25.11";
161161- den.default.homeManager.home.stateVersion = "25.11";
162162- den.default.includes = [ den._.inputs' ];
144144+Built on top of `den.lib`, Den provides a framework with ready-made facilities for
145145+NixOS/nix-Darwin/homes configurations.
163146164164-}
147147+```mermaid
148148+flowchart LR
149149+ Schema["den.hosts / den.homes"] --> Ctx["den.ctx pipeline"]
150150+ Aspects["den.aspects"] --> Ctx
151151+ Batteries["den.provides"] --> Ctx
152152+ Ctx --> Resolve["Resolution"]
153153+ Resolve --> Out["nixos / darwin / homeConfigurations"]
165154```
166155167167-<CardGrid>
168168- <Card title="Declare Systems" icon="laptop">
169169- One-liner [host and user](/guides/declare-hosts/) definitions with
170170- [freeform schemas](/reference/schema/). NixOS, Darwin, Home-Manager —
171171- all from a single source.
172172- </Card>
173173- <Card title="Bidirectional" icon="right-arrow">
174174- Hosts configure their users. Users contribute to their
175175- hosts. [Aspects flow in both directions](/guides/configure-aspects/) automatically.
176176- </Card>
177177- <Card title="Batteries Included" icon="add-document">
178178- Opt-in [aspects for common tasks](/guides/batteries/): define-user, primary-user,
179179- user-shell, unfree packages, import-tree, and more.
180180- </Card>
181181- <Card title="Community" icon="heart">
182182- Share configurations through [namespaces](/guides/namespaces/). Real-world setups
183183- and a growing ecosystem at [denful](https://github.com/vic/denful) (wip).
184184- </Card>
185185-</CardGrid>
156156+<Steps>
157157+1. **Schema** -- `den.hosts` and `den.homes` declare machines, users, and their properties with `den.base` modules and extensible freeform types.
158158+2. **Aspects** -- `den.aspects.*` bundles per-class configs (`nixos`, `darwin`, `homeManager`, or any custom class) with `.includes` and `.provides` forming a DAG.
159159+3. **Context pipeline** -- `den.ctx` transforms schema entries into context pairs (`{host}`, `{host, user}`, `{home}`), walking `into.*` transitions for derived contexts like `hm-host`, `hm-user`, `wsl-host` or any other custom context stage.
160160+4. **Resolution** -- Parametric dispatch via `__functor` argument introspection. Functions receive only contexts whose shape matches their parameters.
161161+5. **Output** -- Each host/home is instantiated via `nixpkgs.lib.nixosSystem`, `darwin.lib.darwinSystem`, or `home-manager.lib.homeManagerConfiguration`.
162162+</Steps>
186163164164+<LinkButton icon="right-arrow" variant="primary" href="/explanation/core-principles">Learn More</LinkButton>
187165188188-Feel free to to **explore** the [codebase](https://github.com/vic/den), particularly our [included batteries](https://github.com/vic/den/tree/main/modules/aspects/provides) and [tests](https://github.com/vic/den/tree/main/templates/ci/modules/features) that serve as examples.
189166190167## Den is made possible by amazing people
191168
+42-222
docs/src/content/docs/overview.mdx
···11---
22title: Documentation Overview
33-description: Everything Den
33+description: Map of all Den documentation sections.
44---
5566-import { Card, CardGrid, LinkButton } from '@astrojs/starlight/components';
66+import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
7788-## Core Concepts
99-1010-<CardGrid>
1111- <Card title="Context Transformation" icon="rocket">
1212- Declarative pipeline that flows configuration through named context types — host, user, home, and custom.
1313- <LinkButton href="/explanation/core-principles" variant="minimal" icon="right-arrow">Learn More</LinkButton>
1414- </Card>
1515- <Card title="Context-Aware Aspects" icon="puzzle">
1616- Aspects are functions of context. A single aspect can target NixOS, Home-Manager, Darwin, and more — resolved automatically by context.
1717- <LinkButton href="/explanation/aspects" variant="minimal" icon="right-arrow">Learn More</LinkButton>
1818- </Card>
1919- <Card title="Context Pipeline" icon="list-format">
2020- Stage-by-stage flow from host entry through user enumeration, HM detection, deduplication, and standalone homes.
2121- <LinkButton href="/explanation/context-pipeline" variant="minimal" icon="right-arrow">Learn More</LinkButton>
2222- </Card>
2323- <Card title="Context System" icon="setting">
2424- Named context types with `den.ctx` schema — providers, includes, transformations, fan-out, and 10+ built-in context types.
2525- <LinkButton href="/explanation/context-system" variant="minimal" icon="right-arrow">Learn More</LinkButton>
2626- </Card>
2727- <Card title="Parametric Aspects" icon="seti:config">
2828- `den.lib.parametric` with matching variants: `atLeast`, `exactly`, `fixedTo`, `expands`, `withOwn`, and `take` functions.
2929- <LinkButton href="/explanation/parametric" variant="minimal" icon="right-arrow">Learn More</LinkButton>
3030- </Card>
3131- <Card title="Library vs Framework" icon="open-book">
3232- Den's dual nature — use the pure library for custom pipelines, or the framework for NixOS/Darwin/HM. All parts optional and replaceable.
3333- <LinkButton href="/explanation/library-vs-framework" variant="minimal" icon="right-arrow">Learn More</LinkButton>
3434- </Card>
3535-</CardGrid>
88+## Learn
3693737-## Configuration & Schema
1010+Understand the principles and mechanisms behind Den.
38113912<CardGrid>
4040- <Card title="Declare Hosts & Users" icon="laptop">
4141- Define hosts with class detection, custom attributes, multiple users, standalone HM, base modules, and custom instantiation.
4242- <LinkButton href="/guides/declare-hosts" variant="minimal" icon="right-arrow">Learn More</LinkButton>
4343- </Card>
4444- <Card title="Configure Aspects" icon="pencil">
4545- Static host config, context-aware includes, bidirectional user/host config, conditional config, global defaults, and named sub-aspects.
4646- <LinkButton href="/guides/configure-aspects" variant="minimal" icon="right-arrow">Learn More</LinkButton>
4747- </Card>
4848- <Card title="Schema Reference" icon="document">
4949- Entity schemas for `den.hosts`, `den.homes`, `den.base` — host options, user options, home options, class detection, and instantiation.
5050- <LinkButton href="/reference/schema" variant="minimal" icon="right-arrow">Learn More</LinkButton>
5151- </Card>
5252- <Card title="Output & Build" icon="seti:favicon">
5353- How Den builds `nixosConfigurations`, `darwinConfigurations`, and `homeConfigurations`. Output placement, main module resolution.
5454- <LinkButton href="/reference/output" variant="minimal" icon="right-arrow">Learn More</LinkButton>
5555- </Card>
5656- <Card title="Aspects Reference" icon="seti:folder-config">
5757- Aspect attribute table, automatic creation from `den.hosts`/`den.homes`, resolution via `flake-aspects`, `den.aspects` and `den.provides`.
5858- <LinkButton href="/reference/aspects" variant="minimal" icon="right-arrow">Learn More</LinkButton>
5959- </Card>
6060- <Card title="Context Reference" icon="seti:pipeline">
6161- All built-in context types in detail: host, user, default, home, hm-host, hm-user, maid-host, maid-user, hjem-host, hjem-user.
6262- <LinkButton href="/reference/ctx" variant="minimal" icon="right-arrow">Learn More</LinkButton>
6363- </Card>
1313+ <LinkCard title="Core Principles" href="/explanation/core-principles/" description="Dendritic design: features over hosts, aspects over modules." />
1414+ <LinkCard title="Context System" href="/explanation/context-system/" description="How den.ctx types define data flow between entities." />
1515+ <LinkCard title="Aspects & Functors" href="/explanation/aspects/" description="The __functor pattern and aspect structure." />
1616+ <LinkCard title="Parametric Aspects" href="/explanation/parametric/" description="Context-aware function dispatch via argument introspection." />
1717+ <LinkCard title="Context Pipeline" href="/explanation/context-pipeline/" description="How host/user/home contexts transform into NixOS/Darwin/HM modules." />
1818+ <LinkCard title="Library vs Framework" href="/explanation/library-vs-framework/" description="Using Den's core lib for any Nix domain." />
6419</CardGrid>
65206666-## Home & User Management
2121+## Cookbook
67226868-<CardGrid>
6969- <Card title="Home Manager Integration" icon="seti:home">
7070- Automatic HM detection, host-managed users, standalone HM, `useGlobalPkgs`, custom modules, `osConfig` access, and context hooks.
7171- <LinkButton href="/guides/home-manager" variant="minimal" icon="right-arrow">Learn More</LinkButton>
7272- </Card>
7373- <Card title="Custom Classes" icon="add-document">
7474- Create new Nix classes with `den._.forward` — hjem, maid, containers, VMs, guarded forward with conditions.
7575- <LinkButton href="/guides/custom-classes" variant="minimal" icon="right-arrow">Learn More</LinkButton>
7676- </Card>
7777- <Card title="User Classes" icon="seti:users">
7878- Default `homeManager` class, multiple user classes, per-class aspect resolution, OS user class forwarding.
7979- <LinkButton href="/tutorials/ci" variant="minimal" icon="right-arrow">Learn More</LinkButton>
8080- </Card>
8181- <Card title="Bidirectional Config" icon="random">
8282- Host-owned config applies to all users; user-owned config applies to all hosts. Static, parametric, and function-based.
8383- <LinkButton href="/guides/configure-aspects" variant="minimal" icon="right-arrow">Learn More</LinkButton>
8484- </Card>
8585- <Card title="Standalone Homes" icon="seti:home">
8686- Standalone `homeConfigurations` output — aspect config, custom username, `den.ctx.home` context, independent of any host.
8787- <LinkButton href="/guides/home-manager" variant="minimal" icon="right-arrow">Learn More</LinkButton>
8888- </Card>
8989- <Card title="Hjem & Maid Classes" icon="seti:folder-config">
9090- Built-in hjem and maid user class support — forwarding, merge with NixOS, automatic class detection.
9191- <LinkButton href="/guides/custom-classes" variant="minimal" icon="right-arrow">Learn More</LinkButton>
9292- </Card>
9393- <Card title="OS User Class" icon="seti:users">
9494- Forward user descriptions, access OS args like `pkgs`, mergeable options across user/host/NixOS boundaries.
9595- <LinkButton href="/reference/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
9696- </Card>
9797-</CardGrid>
9898-9999-## Batteries Included
2323+Practical recipes for common tasks.
1002410125<CardGrid>
102102- <Card title="define-user" icon="seti:users">
103103- `den._.define-user` — creates `isNormalUser`, sets home directory, works at user, host, or default level.
104104- <LinkButton href="/reference/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
105105- </Card>
106106- <Card title="primary-user" icon="star">
107107- `den._.primary-user` — adds wheel and networkmanager groups to the primary user.
108108- <LinkButton href="/reference/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
109109- </Card>
110110- <Card title="user-shell" icon="seti:shell">
111111- `den._.user-shell` — sets the user shell on both OS and Home-Manager simultaneously.
112112- <LinkButton href="/reference/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
113113- </Card>
114114- <Card title="unfree" icon="approve-check">
115115- `den._.unfree` — sets `allowUnfreePredicate` on both NixOS and Home-Manager.
116116- <LinkButton href="/reference/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
117117- </Card>
118118- <Card title="tty-autologin" icon="seti:log">
119119- `den._.tty-autologin` — creates a getty autologin service for the user.
120120- <LinkButton href="/reference/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
121121- </Card>
122122- <Card title="import-tree" icon="seti:folder-src">
123123- `den._.import-tree._.host` — auto-imports host directories based on class, handles missing dirs gracefully.
124124- <LinkButton href="/reference/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
125125- </Card>
126126- <Card title="inputs' & self'" icon="seti:nix">
127127- `den._.inputs'` and `den._.self'` — provide flake-parts special args (`inputs'`, `self'`) inside aspects.
128128- <LinkButton href="/reference/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
129129- </Card>
130130- <Card title="forward" icon="right-arrow">
131131- `den._.forward` — forward aspect config into custom Nix classes with optional guard conditions.
132132- <LinkButton href="/reference/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
133133- </Card>
134134- <Card title="Batteries Guide" icon="open-book">
135135- Overview and usage guide for all built-in batteries — when to use each one, practical examples, and composition patterns.
136136- <LinkButton href="/guides/batteries" variant="minimal" icon="right-arrow">Learn More</LinkButton>
137137- </Card>
2626+ <LinkCard title="Declare Hosts & Users" href="/guides/declare-hosts/" description="den.hosts, den.homes, users, and schema options." />
2727+ <LinkCard title="Configure Aspects" href="/guides/configure-aspects/" description="Owned configs, includes, provides, and defaults." />
2828+ <LinkCard title="Custom Nix Classes" href="/guides/custom-classes/" description="Create new classes via den.provides.forward." />
2929+ <LinkCard title="Homes Integration" href="/guides/home-manager/" description="Home Manager, hjem, and nix-maid setup." />
3030+ <LinkCard title="Use Batteries" href="/guides/batteries/" description="den.provides.* built-in aspects." />
3131+ <LinkCard title="Share with Namespaces" href="/guides/namespaces/" description="Publish and consume aspect libraries across flakes." />
3232+ <LinkCard title="Angle Brackets Syntax" href="/guides/angle-brackets/" description="<aspect/path> shorthand via __findFile." />
3333+ <LinkCard title="Migrate to Den" href="/guides/migrate/" description="Incremental migration from existing setups." />
3434+ <LinkCard title="Debug Configurations" href="/guides/debug/" description="REPL inspection, tracing, and troubleshooting." />
13835</CardGrid>
13936140140-## Library API
141141-142142-<CardGrid>
143143- <Card title="den.lib Reference" icon="seti:code-search">
144144- Full API: `parametric`, `take`, `canTake`, `aspects`, `isFn`, `owned`, `statics`, `isStatic`, `__findFile`.
145145- <LinkButton href="/reference/lib" variant="minimal" icon="right-arrow">Learn More</LinkButton>
146146- </Card>
147147- <Card title="Angle Brackets" icon="seti:bracket-dot">
148148- Opt-in `<den/...>` shorthand — access `<den.lib>`, `<den/import-tree/host>`, namespaces like `<ns/path>`.
149149- <LinkButton href="/guides/angle-brackets" variant="minimal" icon="right-arrow">Learn More</LinkButton>
150150- </Card>
151151- <Card title="Namespaces" icon="seti:folder-dist">
152152- Define local namespaces, consume remote ones, nested provides, expose as flake output, merge multiple sources.
153153- <LinkButton href="/guides/namespaces" variant="minimal" icon="right-arrow">Learn More</LinkButton>
154154- </Card>
155155- <Card title="Cross-Provider Contexts" icon="random">
156156- `den.ctx.*.into.*` cross-provider derivation — transform config between context types, per-value providers.
157157- <LinkButton href="/reference/ctx" variant="minimal" icon="right-arrow">Learn More</LinkButton>
158158- </Card>
159159- <Card title="Host Propagation" icon="list-format">
160160- Full propagation: owned, static, lax, exact, atLeast contexts with default includes, hm-host, and hm-user stages.
161161- <LinkButton href="/explanation/context-pipeline" variant="minimal" icon="right-arrow">Learn More</LinkButton>
162162- </Card>
163163- <Card title="Default Includes" icon="seti:default">
164164- `den.default.includes` — global defaults applied to all hosts/users, dynamic class resolution, hostname from context.
165165- <LinkButton href="/explanation/context-pipeline" variant="minimal" icon="right-arrow">Learn More</LinkButton>
166166- </Card>
167167- <Card title="Schema Base Modules" icon="document">
168168- `den.base.conf`, `den.base.host`, `den.base.user`, `den.base.home` — extend entity schemas with custom modules.
169169- <LinkButton href="/reference/schema" variant="minimal" icon="right-arrow">Learn More</LinkButton>
170170- </Card>
171171- <Card title="Custom Instantiation" icon="setting">
172172- Override `instantiate` for standalone HM with `osConfig` special arg, or custom deployment targets.
173173- <LinkButton href="/guides/declare-hosts" variant="minimal" icon="right-arrow">Learn More</LinkButton>
174174- </Card>
175175- <Card title="Conditional Config" icon="approve-check">
176176- Conditional NixOS imports based on host/user attributes. Per-user HM config. Static aspects in defaults.
177177- <LinkButton href="/guides/configure-aspects" variant="minimal" icon="right-arrow">Learn More</LinkButton>
178178- </Card>
179179- <Card title="External Providers" icon="external">
180180- Consume aspects from external flakes. Provider flake integration, deep nested namespace aspects.
181181- <LinkButton href="/guides/namespaces" variant="minimal" icon="right-arrow">Learn More</LinkButton>
182182- </Card>
183183-</CardGrid>
3737+## Templates
18438185185-## Getting Started
3939+Step-by-step walkthroughs for each starter template.
1864018741<CardGrid>
188188- <Card title="Minimal Template" icon="seti:mint">
189189- Smallest possible setup — one host, one user, no extra deps. Single `flake.nix` + `modules/den.nix`.
190190- <LinkButton href="/tutorials/minimal" variant="minimal" icon="right-arrow">Learn More</LinkButton>
191191- </Card>
192192- <Card title="Default Template" icon="star">
193193- Recommended starting point with flake-parts, Home-Manager, and VM testing. Full file walkthrough.
194194- <LinkButton href="/tutorials/default" variant="minimal" icon="right-arrow">Learn More</LinkButton>
195195- </Card>
196196- <Card title="Example Template" icon="seti:code-climate">
197197- Advanced: cross-platform hosts, namespaces, angle brackets, bidirectional providers, custom routes.
198198- <LinkButton href="/tutorials/example" variant="minimal" icon="right-arrow">Learn More</LinkButton>
199199- </Card>
200200- <Card title="No-Flake Template" icon="seti:lock">
201201- Using Den with stable Nix via npins. No flakes required. Works with nix-maid.
202202- <LinkButton href="/tutorials/noflake" variant="minimal" icon="right-arrow">Learn More</LinkButton>
203203- </Card>
204204- <Card title="Bogus / Bug Report" icon="warning">
205205- Bug reproduction template with nix-unit. `denTest` helper and available test fixtures.
206206- <LinkButton href="/tutorials/bogus" variant="minimal" icon="right-arrow">Learn More</LinkButton>
207207- </Card>
208208- <Card title="Templates Overview" icon="open-book">
209209- Comparison table of all 6 templates, quick start commands, project structure.
210210- <LinkButton href="/tutorials/overview" variant="minimal" icon="right-arrow">Learn More</LinkButton>
211211- </Card>
4242+ <LinkCard title="Templates Overview" href="/tutorials/overview/" description="Comparison of all available templates." />
4343+ <LinkCard title="Default" href="/tutorials/default/" description="Recommended: flake-parts + home-manager." />
4444+ <LinkCard title="Minimal" href="/tutorials/minimal/" description="Minimal flake, no flake-parts." />
4545+ <LinkCard title="Example" href="/tutorials/example/" description="Cross-platform NixOS + Darwin." />
4646+ <LinkCard title="No-Flake" href="/tutorials/noflake/" description="npins + lib.evalModules." />
4747+ <LinkCard title="CI Tests" href="/tutorials/ci/" description="Feature tests as executable code examples." />
4848+ <LinkCard title="Bug Reproduction" href="/tutorials/bogus/" description="Isolated template for bug reports." />
21249</CardGrid>
21350214214-## Advanced & Operations
5151+## Reference
5252+5353+API documentation for every Den option.
2155421655<CardGrid>
217217- <Card title="Migration Guide" icon="right-caret">
218218- Incremental adoption from flake-parts, import existing modules, mix Den with existing `nixosSystem`, recommended path.
219219- <LinkButton href="/guides/migrate" variant="minimal" icon="right-arrow">Learn More</LinkButton>
220220- </Card>
221221- <Card title="Debugging" icon="warning">
222222- `builtins.trace`, `builtins.break`, REPL inspection, manually resolving aspects, common issues.
223223- <LinkButton href="/guides/debug" variant="minimal" icon="right-arrow">Learn More</LinkButton>
224224- </Card>
225225- <Card title="CI Test Suite" icon="seti:test">
226226- 40+ test files covering every feature. Run tests, write new ones, understand test harness (`denTest`, `evalDen`).
227227- <LinkButton href="/tutorials/ci" variant="minimal" icon="right-arrow">Learn More</LinkButton>
228228- </Card>
5656+ <LinkCard title="den.ctx" href="/reference/ctx/" description="Context types, into transformations, provides." />
5757+ <LinkCard title="den.lib" href="/reference/lib/" description="parametric, canTake, take, statics, owned, __findFile." />
5858+ <LinkCard title="den.aspects" href="/reference/aspects/" description="Aspect type, resolution, class configs." />
5959+ <LinkCard title="den.base" href="/reference/schema/" description="Host, user, home schema options." />
6060+ <LinkCard title="den.provides" href="/reference/batteries/" description="Built-in batteries reference." />
6161+ <LinkCard title="flake.*" href="/reference/output/" description="Output generation and instantiation." />
22962</CardGrid>
2306323164## Community & Project
2326523366<CardGrid>
234234- <Card title="Motivation & History" icon="heart">
235235- Why Den exists — from vic/vix through blueprint, snowfall, and flake-parts to dendritic design.
236236- <LinkButton href="/motivation" variant="minimal" icon="right-arrow">Learn More</LinkButton>
237237- </Card>
238238- <Card title="Community" icon="github">
239239- GitHub Discussions, Zulip Chat, Matrix channel, real-world examples, ecosystem projects.
240240- <LinkButton href="/community" variant="minimal" icon="right-arrow">Learn More</LinkButton>
241241- </Card>
242242- <Card title="Contributing" icon="add-document">
243243- Report bugs, contribute PRs, run tests, format code.
244244- <LinkButton href="/contributing" variant="minimal" icon="right-arrow">Learn More</LinkButton>
245245- </Card>
246246- <Card title="Sponsor" icon="heart">
247247- Support Den development via GitHub Sponsors.
248248- <LinkButton href="/sponsor" variant="minimal" icon="right-arrow">Learn More</LinkButton>
249249- </Card>
6767+ <LinkCard title="Community" href="/community/" description="Discussions, real-world examples, ecosystem." />
6868+ <LinkCard title="Contributing" href="/contributing/" description="Tests, CI, code style." />
6969+ <LinkCard title="Sponsor" href="/sponsor/" description="Support Den development." />
25070</CardGrid>
+73-46
docs/src/content/docs/reference/aspects.mdx
···11---
22-title: den.aspects Reference
33-description: Aspect structure, resolution, and configuration.
22+title: den.aspects
33+description: Aspect type system, resolution, class configuration, and includes dispatch.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">[`modules/aspects.nix`](https://github.com/vic/den/blob/main/modules/aspects.nix) · [`modules/aspects/definition.nix`](https://github.com/vic/den/blob/main/modules/aspects/definition.nix) · [`modules/aspects/provides.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides.nix) · Tests: [`user-host-bidirectional-config.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/user-host-bidirectional-config.nix) · [`top-level-parametric.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/top-level-parametric.nix)</Aside>
88+<Aside title="Source" icon="github">
99+[`modules/aspects.nix`](https://github.com/vic/den/blob/main/modules/aspects.nix) --
1010+[`modules/aspects/definition.nix`](https://github.com/vic/den/blob/main/modules/aspects/definition.nix) --
1111+[`modules/aspects/provides.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides.nix)
1212+</Aside>
9131010-## Aspect Attributes
1414+## `den.aspects`
11151212-Every aspect is an attribute set with these recognized keys:
1616+Type: `aspectsType` (from `flake-aspects`)
13171414-| Attribute | Type | Description |
1515-|-----------|------|-------------|
1616-| `description` | str | Human-readable description |
1717-| `__functor` | function | Context-aware behavior |
1818-| `includes` | list | Dependencies on other aspects or functions |
1919-| `provides` | attrset | Nested sub-aspects |
2020-| `_` | alias | Shorthand for `provides` |
2121-| `<class>` | module | Any Nix class, like the following |
2222-| `nixos` | module | NixOS configuration module |
2323-| `darwin` | module | nix-darwin configuration module |
2424-| `homeManager` | module | Home-Manager configuration module |
1818+An attribute set of aspects. Each aspect key names an aspect; its value is
1919+an aspect set containing per-class config and an `includes` list:
25202626-## Automatic Aspect Creation
2121+```nix
2222+den.aspects = {
2323+ dns = {
2424+ nixos.services.resolved.enable = true;
2525+ darwin.networking.dns = [ "1.1.1.1" ];
2626+ includes = [ ./dns ];
2727+ };
2828+};
2929+```
27302828-Den creates an aspect for each host, user, and home you declare:
3131+### Auto-generation
3232+3333+Den auto-generates `den.aspects` entries from `den.hosts`, `den.homes`,
3434+and `den.users`. For every declared host/home/user, an aspect is created
3535+with the appropriate class configurations. You do not need to declare
3636+`den.aspects` manually unless adding shared aspects.
3737+3838+## `den.ful`
3939+4040+Type: `attrsOf aspectsType`
4141+4242+Namespaced aspect collections. Each key is a namespace name, each value
4343+is a full `aspectsType`. Populated by `den.namespace` or by merging
4444+upstream `denful` flake outputs.
29453046```nix
3131-den.hosts.x86_64-linux.igloo.users.tux = { };
4747+den.ful.myns = {
4848+ some-aspect = { nixos.services.foo.enable = true; };
4949+};
5050+```
5151+5252+## `flake.denful`
32533333-# will create:
5454+Type: `attrsOf raw`
34553535-den.aspects.igloo = parametric { };
3636-den.aspects.tux = parametric { };
3737-```
5656+Raw flake output for publishing namespaces. Set automatically by
5757+`den.namespace`; consumed by downstream flakes that import your aspects.
38583939-## Aspect Resolution
5959+## Aspect structure
40604141-To extract a class module from an aspect use the flake-aspects API:
6161+An aspect is an attribute set with:
42624343-```nix
4444-module = aspect.resolve { class = "nixos"; aspect-chain = []; };
4545-```
6363+| Key | Purpose |
6464+|-----|---------|
6565+| `<class>` | Config merged into hosts/homes of that class |
6666+| `includes` | List of modules or functions dispatched by context |
6767+| `__functor` | Auto-generated by `parametric`; drives dispatch |
46684747-Resolution collects the specified class from the aspect and all
4848-transitive includes into a single merged Nix module.
6969+### Static vs parametric includes
49705050-## den.aspects (namespace)
7171+Functions in `includes` receiving `{ class, aspect-chain }` are **static** --
7272+evaluated once during aspect resolution. Functions receiving context
7373+arguments (`{ host }`, `{ user }`, etc.) are **parametric** -- evaluated
7474+per context during `ctxApply`.
51755252-This namespace is where Den creates aspects for your host/user/home.
7676+## `den.provides`
53775454-You can also use this namespace to create your own aspects.
5555-Aspects in this namespace are not shared outside the flake,
5656-for that use `den.namespace`.
7878+Type: freeform `attrsOf providerType` (aliased as `den._`)
57795858-Contributions from any module are merged:
8080+Batteries-included reusable aspects. Each provider is a `providerType`
8181+from `flake-aspects`. See [Batteries Reference](/reference/batteries/).
59826083```nix
6161-# file1.nix
6262-den.aspects.igloo.nixos.networking.hostName = "igloo";
6363-6464-# file2.nix
6565-den.aspects.igloo.homeManager.programs.vim.enable = true;
8484+den._ = {
8585+ my-battery = {
8686+ nixos.services.something.enable = true;
8787+ includes = [ ./my-module.nix ];
8888+ };
8989+};
6690```
67916868-## den.provides (namespace)
9292+## Class resolution
69937070-Den batteries-included re-usable aspects.
7171-7272-Access via `den._.name` or `den.provides.name`.
9494+When aspects are resolved for a host, Den:
73957474-See [Batteries Reference](/reference/batteries/) for all built-in aspects.
9696+1. Collects all aspects referenced by the host
9797+2. Extracts the class-specific config (e.g., `nixos` for NixOS hosts)
9898+3. Evaluates static includes with `{ class, aspect-chain }`
9999+4. Builds context pairs from `den.ctx`
100100+5. Applies parametric includes via `ctxApply`
101101+6. Merges everything into the host's `evalModules` call
+70-149
docs/src/content/docs/reference/batteries.mdx
···11---
22-title: Batteries Reference
33-description: All built-in opt-in aspects shipped with Den.
22+title: Batteries
33+description: All den.provides batteries -- reusable aspect providers shipped with Den.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">[`modules/aspects/provides/`](https://github.com/vic/den/tree/main/modules/aspects/provides) · Tests: [`batteries/`](https://github.com/vic/den/tree/main/templates/ci/modules/features/batteries)</Aside>
88+<Aside title="Source" icon="github">
99+[`modules/aspects/provides/`](https://github.com/vic/den/blob/main/modules/aspects/provides/)
1010+</Aside>
9111010-## Overview
1212+Den ships reusable aspect providers under `den.provides` (aliased `den._`).
1313+Each battery contributes NixOS/Darwin/home-manager modules to hosts and
1414+users that include them via `den.ctx`.
11151212-Batteries are pre-built aspects at `den.provides` (accessed via `den._.<name>`).
1313-All are opt-in — include them explicitly where needed.
1616+## System batteries
14171515-## define-user
1818+### `den._.define-user`
16191717-Defines a user at OS and Home-Manager levels.
1818-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/define-user.nix) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/define-user.nix))
2020+Creates OS-level user accounts (`users.users.<name>`) with `isNormalUser`,
2121+`home`, and group. Sets `home-manager.users.<name>` when home-manager
2222+integration is active.
19232020-```nix
2121-den.default.includes = [ den._.define-user ];
2222-```
2424+### `den._.os-user`
23252424-**Sets:**
2525-- `users.users.<name>.{name, home, isNormalUser}` (NixOS)
2626-- `users.users.<name>.{name, home}` (Darwin)
2727-- `home.{username, homeDirectory}` (Home-Manager)
2626+Assigns a user into the host by setting group membership and ensuring
2727+the user account exists at the OS level.
28282929-**Contexts:** `{ host, user }`, `{ home }`
2929+### `den._.primary-user`
30303131-## primary-user
3131+Marks a user as the primary user of a host. Sets `nix.settings.trusted-users`
3232+and can configure auto-login or default shell based on other batteries.
32333333-Makes a user an administrator.
3434-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/primary-user.nix) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/primary-user.nix))
3434+### `den._.user-shell`
35353636-```nix
3737-den.aspects.vic.includes = [ den._.primary-user ];
3838-```
3636+Sets the user's login shell. Reads `user.shell` and configures both
3737+`users.users.<name>.shell` and the home-manager `programs.<shell>.enable`.
39384040-**Sets:**
4141-- NixOS: `users.users.<name>.extraGroups = [ "wheel" "networkmanager" ]`
4242-- Darwin: `system.primaryUser = <name>`
4343-- WSL: `wsl.defaultUser = <name>` (if host has `wsl` attribute)
3939+### `den._.tty-autologin`
44404545-**Context:** `{ host, user }`
4141+Configures TTY auto-login for the primary user via
4242+`services.getty.autologinUser`.
46434747-## user-shell
4444+### `den._.wsl`
48454949-Sets default shell at OS and HM levels.
5050-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/user-shell.nix) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/user-shell.nix))
4646+WSL-specific configuration. Enables `wsl.enable`, sets the default user,
4747+and adjusts networking for WSL2 environments.
51485252-```nix
5353-den.aspects.vic.includes = [ (den._.user-shell "fish") ];
5454-```
4949+### `den._.forward`
55505656-**Sets:**
5757-- `programs.<shell>.enable = true` (NixOS/Darwin)
5858-- `users.users.<name>.shell = pkgs.<shell>` (NixOS/Darwin)
5959-- `programs.<shell>.enable = true` (Home-Manager)
5151+Forwards aspect configuration from one aspect to another. Used to chain
5252+aspects without duplicating declarations.
60536161-**Contexts:** `{ host, user }`, `{ home }`
5454+### `den._.import-tree`
62556363-## unfree
5656+Uses `inputs.import-tree` to recursively import a directory tree as
5757+aspect modules. Enables file-system-driven aspect organization.
64586565-Enables unfree packages by name.
6666-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/unfree/unfree-predicate-builder.nix) · [predicate](https://github.com/vic/den/blob/main/modules/aspects/provides/unfree/unfree.nix) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/unfree.nix))
5959+## Flake-parts batteries
67606868-```nix
6969-den.aspects.laptop.includes = [ (den._.unfree [ "discord" ]) ];
7070-```
6161+### `den._.inputs'`
71627272-**Sets:** `unfree.packages` option + `nixpkgs.config.allowUnfreePredicate`
6363+Exposes `inputs'` (system-qualified inputs) into aspect modules.
6464+Source: `provides/flake-parts/inputs.nix`
73657474-**Contexts:** All (host, user, home) — works for any class.
6666+### `den._.self'`
75677676-:::note[useGlobalPkgs interaction]
7777-When Home-Manager's `useGlobalPkgs` is `true`, the unfree module
7878-skips setting `nixpkgs.config` on the HM class to avoid conflicts.
7979-Set unfree packages on the **host** aspect instead.
8080-:::
6868+Exposes `self'` (system-qualified self outputs) into aspect modules.
6969+Source: `provides/flake-parts/self.nix`
81708282-## tty-autologin
7171+## Home-manager batteries
83728484-Automatic tty login.
8585-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/tty-autologin.nix) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/tty-autologin.nix))
7373+### `den._.hm-integration`
86748787-```nix
8888-den.aspects.laptop.includes = [ (den._.tty-autologin "root") ];
8989-```
7575+Integrates home-manager into NixOS/Darwin hosts. Imports the appropriate
7676+home-manager module (`nixos` or `darwin`), enables
7777+`home-manager.useGlobalPkgs` and `useUserPackages`.
90789191-**Sets:** `systemd.services."getty@tty1"` with autologin.
7979+### `den._.hm-os`
92809393-**Class:** NixOS only.
8181+Merges home-manager configuration into the host OS module system. Bridges
8282+`home-manager.users.<name>` with the user's aspect.
94839595-## import-tree
8484+## Hjem batteries
96859797-Auto-imports non-dendritic Nix files by class directory.
9898-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/import-tree.nix) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/import-tree.nix))
8686+### `den._.hjem-integration`
9987100100-```nix
101101-den.aspects.laptop.includes = [ (den._.import-tree ./path) ];
102102-```
8888+Integrates [hjem](https://github.com/feel-co/hjem) as an alternative to
8989+home-manager. Imports hjem's NixOS module.
10390104104-Looks for `./path/_nixos/`, `./path/_darwin/`, `./path/_homeManager/`.
9191+### `den._.hjem-os`
10592106106-**Helpers:**
9393+Merges hjem user configuration into the host. Sets `hjem.users.<name>`
9494+from the user's aspect.
10795108108-```nix
109109-den._.import-tree._.host ./hosts # per host: ./hosts/<name>/_<class>
110110-den._.import-tree._.user ./users # per user: ./users/<name>/_<class>
111111-den._.import-tree._.home ./homes # per home: ./homes/<name>/_<class>
112112-```
9696+## Maid batteries
11397114114-**Requires:** `inputs.import-tree`
9898+### `den._.maid-integration`
11599116116-## inputs' (flake-parts)
100100+Integrates [maid](https://github.com/maid-nix/maid) as an alternative
101101+home management system.
117102118118-Provides per-system `inputs'` as a module argument.
119119-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/flake-parts/inputs.nix) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/flake-parts.nix))
103103+### `den._.maid-os`
120104121121-```nix
122122-den.default.includes = [ den._.inputs' ];
123123-```
105105+Merges maid configuration into the host OS for each user.
124106125125-**Requires:** flake-parts with `withSystem`.
107107+## Unfree batteries
126108127127-## self' (flake-parts)
109109+### `den._.unfree`
128110129129-Provides per-system `self'` as a module argument.
130130-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/flake-parts/self.nix) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/flake-parts.nix))
111111+Allows unfree packages globally via
112112+`nixpkgs.config.allowUnfree = true`.
131113132132-```nix
133133-den.default.includes = [ den._.self' ];
134134-```
114114+### `den._.unfree-predicate`
135115136136-**Requires:** flake-parts with `withSystem`.
137137-138138-## forward
139139-140140-Creates custom Nix classes by forwarding configs between classes.
141141-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/forward.nix) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/forward-from-custom-class.nix) · [usage guide](/guides/custom-classes/))
142142-143143-```nix
144144-den._.forward {
145145- each = lib.singleton class;
146146- fromClass = _: "source";
147147- intoClass = _: "nixos";
148148- intoPath = _: [ "target" "path" ];
149149- fromAspect = _: sourceAspect;
150150- guard = { options, ... }: options ? target; # optional
151151-}
152152-```
153153-154154-Returns an aspect. Supports optional `guard` for conditional forwarding.
155155-Used internally for Home-Manager, nix-maid, and hjem integrations.
156156-157157-## home-manager
158158-159159-HM integration is handled by
160160-`den.ctx.hm-host` and `den.ctx.hm-user` context types, which are
161161-activated automatically when hosts have HM users.
162162-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/) · [tests](https://github.com/vic/den/blob/main/templates/ci/modules/features/home-manager/))
163163-164164-All `homeManager` class settings are forwarded to os-level `home-manager.users.<userName>`.
165165-166166-## maid
167167-168168-nix-maid integration is handled by `den.ctx.maid-host` and `den.ctx.maid-user`,
169169-activated when hosts have users with `"maid"` in their `classes`.
170170-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/maid/))
171171-172172-Forwards `maid` class settings to os-level `users.users.<userName>.maid`.
173173-Requires `inputs.nix-maid` or a custom `host.maid-module`.
174174-175175-## hjem
176176-177177-hjem integration is handled by `den.ctx.hjem-host` and `den.ctx.hjem-user`,
178178-activated when hosts have users with `"hjem"` in their `classes`.
179179-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/hjem/))
180180-181181-Forwards `hjem` class settings to os-level `hjem.users.<userName>`.
182182-Requires `inputs.hjem` or a custom `host.hjem-module`.
183183-184184-## user
185185-186186-The `user` class is automatically handled by Den and forwards to os-level `users.users.<userName>`
187187-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/os-user.nix))
188188-189189-You can write:
190190-191191-```nix
192192-den.aspects.tux.user.description = "bird";
193193-194194-# same as setting at host:
195195-den.aspects.igloo.nixos.users.users.tux.description = "bird";
196196-```
197197-116116+Allows unfree packages selectively using a predicate builder. Accepts
117117+a list of package names and builds
118118+`nixpkgs.config.allowUnfreePredicate`.
+86-153
docs/src/content/docs/reference/ctx.mdx
···11---
22-title: den.ctx Reference
33-description: Context type definitions and their built-in implementations.
22+title: den.ctx
33+description: Context types, into transformations, and provides.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">[`modules/context/types.nix`](https://github.com/vic/den/blob/main/modules/context/types.nix) · [`modules/context/os.nix`](https://github.com/vic/den/blob/main/modules/context/os.nix) · [`modules/aspects/defaults.nix`](https://github.com/vic/den/blob/main/modules/aspects/defaults.nix) · Tests: [`context/`](https://github.com/vic/den/tree/main/templates/ci/modules/features/context) · [`host-propagation.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/host-propagation.nix)</Aside>
99-1010-## Context Type Schema
1111-1212-Each `den.ctx.<name>` is an [aspect submodule](/explanation/aspects/) extended
1313-with context transformations. It inherits all options from `flake-aspects`'s
1414-`aspect`-type and adds `into`, and a couple of conventions:
1515-1616-| Option | Type | Description |
1717-|--------|------|-------------|
1818-| `description` | `str` | Human-readable description |
1919-| `provides.${name}` | `ctx → aspect` | Self-named provider: locates the aspect for this context |
2020-| `provides.${target}` | `ctx → aspect` | Cross-provider: source's contribution to target contexts |
2121-| `into` | `attrset of (ctx → list)` | Transformations to other contexts |
2222-| `includes` | `list of aspect` | Parametric aspects activated for this context |
88+<Aside title="Source" icon="github">
99+[`modules/context/types.nix`](https://github.com/vic/den/blob/main/modules/context/types.nix) --
1010+[`modules/context/os.nix`](https://github.com/vic/den/blob/main/modules/context/os.nix)
1111+</Aside>
23122424-Context types are also functors — callable as functions:
1313+## `den.ctx`
25142626-```nix
2727-aspect = den.ctx.host { host = den.hosts.x86_64-linux.igloo; };
2828-```
1515+Type: `lazyAttrsOf ctxType`
29163030-## ctxApply (internal)
1717+A map of context type names to context type definitions. Each context type
1818+defines how data flows through the evaluation pipeline.
31193232-When a context type is applied, `ctxApply` walks the full `into` graph
3333-and deduplicates includes using a seen-set:
2020+## Context Type Options
34213535-```nix
3636-ctxApply = ctxName: _self: ctx:
3737- let
3838- pairs = collectPairs null den.ctx.${ctxName} ctx;
3939- in
4040- { includes = dedupIncludes pairs; };
4141-```
2222+Each `den.ctx.<name>` has:
42234343-**collectPairs** recursively walks `into` transformations, producing
4444-`(source, ctxDef, ctx)` triples for every reachable context type.
4545-The `source` tracks which context type produced the transformation.
2424+### `description`
46254747-**dedupIncludes** processes triples with a seen-set keyed by context name:
2626+Type: `str`
48274949-- **First visit**: `fixedTo ctx (cleanCtx ctxDef)` — dispatches owned, static, and parametric includes — plus self-provider and cross-provider
5050-- **Subsequent visits**: `atLeast (cleanCtx ctxDef) ctx` — dispatches only parametric includes — plus self-provider and cross-provider
2828+Human-readable description of this context type.
51295252-For each triple, three providers are called:
5353-1. **Self-provider** `ctxDef.provides.${ctxDef.name}` — the target's own aspect lookup
5454-2. **Cross-provider** `source.provides.${ctxDef.name}` — the source's contribution to this target (if defined)
3030+### `_` (alias: `provides`)
55315656-## Transformation Types
3232+Type: `attrsOf providerType`
57335858-All `into` transformations have the type `source → [ target ]`:
3434+Maps context names to provider functions. When this context is processed,
3535+each provider is called with the current context data and returns aspect fragments.
59366060-- **Fan-out**: `{ host }: map (u: { inherit host user; }) users` — one host produces N contexts
6161-- **Conditional**: `{ host }: lib.optional (test host) { inherit host; }` — zero or one
6262-- **Pass-through**: `lib.singleton` — forward the same data as-is
3737+```nix
3838+den.ctx.host._.host = { host }:
3939+ parametric.fixedTo { inherit host; } den.aspects.${host.aspect};
4040+```
63416464-An empty list means the target context is not created. This enables
6565-conditional activation like HM detection without any explicit `if` logic
6666-in the pipeline.
4242+### `into`
67436868-## Contexts as Cutting-Points
4444+Type: `lazyAttrsOf (functionTo (listOf raw))`
69457070-Context types have their own owned configs and `includes`, making them
7171-aspect-like cutting-points in the pipeline:
4646+Maps other context type names to transformation functions. Each function
4747+takes the current context data and returns a list of new context values.
72487349```nix
7474-den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true;
7575-den.ctx.hm-host.includes = [
7676- ({ host, ... }: { nixos.home-manager.backupFileExtension = "bak"; })
7777-];
5050+den.ctx.host.into.user = { host }:
5151+ map (user: { inherit host user; }) (lib.attrValues host.users);
7852```
79535454+### `includes`
80558181-## Extending Context Flow
8282-8383-Add transformations to existing context types from any module:
5656+Aspect includes attached to this context type. Used by batteries to inject
5757+behavior at specific pipeline stages.
84588585-```nix
8686-den.ctx.hm-host.into.foo = { host }: [ { foo = host.name; } ];
8787-den.ctx.foo._.foo = { foo }: { funny.names = [ foo ]; };
8888-```
5959+### `modules`
89609090-The module system merges these definitions. You can also override a
9191-host's `mainModule` to use a completely custom context flow.
6161+Additional modules merged into the resolved output.
92629363## Built-in Context Types
94649595-### den.ctx.host
6565+### `den.ctx.host`
96669797-| Field | Value |
9898-|-------|-------|
9999-| `description` | OS |
100100-| `provides.host` | `{ host }:` fixedTo host aspect |
101101-| `provides.user` | `{ host, user }:` host's contribution to user contexts (cross-provider) |
102102-| `into.default` | `lib.singleton` (pass-through) |
103103-| `into.user` | Enumerate `host.users` |
104104-| `into.hm-host` | Detect HM support (from `hm-os.nix`) |
105105-| `into.maid-host` | Detect nix-maid support (from `maid-os.nix`) |
106106-| `into.hjem-host` | Detect hjem support (from `hjem-os.nix`) |
6767+Context data: `{ host }`
10768108108-### den.ctx.user
6969+Produced for each `den.hosts.<system>.<name>` entry.
10970110110-| Field | Value |
111111-|-------|-------|
112112-| `description` | OS user |
113113-| `provides.user` | `{ host, user }:` fixedTo user aspect |
114114-| `into.default` | `lib.singleton` (pass-through) |
7171+Providers:
7272+- `_.host` -- `fixedTo { host }` on the host's aspect.
7373+- `_.user` -- `atLeast` on the host's aspect with `{ host, user }`.
11574116116-### den.ctx.default
7575+Transitions:
7676+- `into.default` -- identity (for default aspect).
7777+- `into.user` -- one `{ host, user }` per `host.users` entry.
7878+- `into.hm-host` -- (from `hm-os.nix`) if HM enabled and has HM users.
7979+- `into.wsl-host` -- (from `wsl.nix`) if WSL enabled on NixOS host.
8080+- `into.hjem-host` -- (from `hjem-os.nix`) if hjem enabled.
8181+- `into.maid-host` -- (from `maid-os.nix`) if nix-maid enabled.
11782118118-| Field | Value |
119119-|-------|-------|
120120-| `provides.default` | `_: { }` (empty — just a dispatcher) |
8383+### `den.ctx.user`
12184122122-Aliased as `den.default` via `lib.mkAliasOptionModule`.
123123-Writing `den.default.foo` is identical to `den.ctx.default.foo`.
8585+Context data: `{ host, user }`
12486125125-Host/user/home context types transforms into `default`, so `den.default.includes`
126126-functions run at each of their pipeline stages. However, owned and static configs
127127-are **automatically deduplicated** — only the first visit gets them.
128128-Parametric functions still run at every stage; use `take.exactly` to
129129-restrict matching if needed.
8787+Providers:
8888+- `_.user` -- `fixedTo { host, user }` on the user's aspect.
13089131131-### den.ctx.home
9090+Transitions:
9191+- `into.default` -- identity.
13292133133-| Field | Value |
134134-|-------|-------|
135135-| `description` | Standalone Home-Manager config |
136136-| `provides.home` | `{ home }:` fixedTo home aspect |
137137-| `into.default` | `lib.singleton` |
9393+### `den.ctx.home`
13894139139-### den.ctx.hm-host
9595+Context data: `{ home }`
14096141141-| Field | Value |
142142-|-------|-------|
143143-| `description` | Host with HM-supported OS and HM users |
144144-| `provides.hm-host` | `{ host }:` imports HM NixOS/Darwin module |
145145-| `into.hm-user` | Enumerate HM-class users |
9797+Produced for each `den.homes.<system>.<name>` entry.
14698147147-**Detection criteria** (all must be true):
148148-1. Host class is `nixos` or `darwin`
149149-2. At least one user has `"homeManager"` in their `classes`
150150-3. `inputs.home-manager` exists, or host has a custom `hm-module` attribute
9999+Providers:
100100+- `_.home` -- `fixedTo { home }` on the home's aspect.
151101152152-If detection fails, the HM pipeline is skipped entirely.
102102+### `den.ctx.hm-host`
153103154154-### den.ctx.hm-user
104104+Context data: `{ host }`
155105156156-| Field | Value |
157157-|-------|-------|
158158-| `description` | Internal — forwards HM class to host |
159159-| `provides.hm-user` | `{ host, user }:` forward homeManager into host |
106106+Providers:
107107+- `provides.hm-host` -- imports HM OS module.
160108161161-### den.ctx.hm-internal-user (internal)
109109+Transitions:
110110+- `into.hm-user` -- per HM-class user.
162111163163-An internal context type used by `hm-user`. It combines:
164164-- The user context (`den.ctx.user { host, user }`)
165165-- Owned configs from the host aspect
166166-- Static includes from the host aspect
167167-- Parametric includes from the host aspect matching `{ host, user }`
112112+### `den.ctx.hm-user`
168113169169-This ensures that when the `homeManager` class is forwarded into
170170-`home-manager.users.<name>`, it receives contributions from both
171171-the user's aspect and the host's aspect.
114114+Context data: `{ host, user }`
172115173173-### den.ctx.maid-host
116116+Providers:
117117+- `_.hm-user` -- forwards `homeManager` class to `home-manager.users.<userName>`.
174118175175-| Field | Value |
176176-|-------|-------|
177177-| `description` | Host with nix-maid users |
178178-| `provides.maid-host` | `{ host }:` imports nix-maid NixOS module |
179179-| `into.maid-user` | Enumerate maid-class users |
119119+### `den.ctx.wsl-host`
180120181181-**Detection criteria** (all must be true):
182182-1. Host class is `nixos` or `darwin`
183183-2. At least one user has `"maid"` in their `classes`
184184-3. `inputs.nix-maid` exists, or host has a custom `maid-module` attribute
121121+Context data: `{ host }`
185122186186-### den.ctx.maid-user
123123+Providers:
124124+- `provides.wsl-host` -- imports WSL module, creates `wsl` class forward.
187125188188-| Field | Value |
189189-|-------|-------|
190190-| `provides.maid-user` | `{ host, user }:` forward maid class to `users.users.<name>.maid` |
126126+## Custom Context Types
191127192192-### den.ctx.hjem-host
128128+Define new contexts to extend the pipeline:
193129194194-| Field | Value |
195195-|-------|-------|
196196-| `description` | Host with hjem users |
197197-| `provides.hjem-host` | `{ host }:` imports hjem NixOS module |
198198-| `into.hjem-user` | Enumerate hjem-class users |
130130+```nix
131131+{
132132+ den.ctx.gpu = {
133133+ description = "GPU-enabled host";
134134+ _.gpu = { host }: {
135135+ nixos.hardware.nvidia.enable = true;
136136+ };
137137+ };
199138200200-**Detection criteria** (all must be true):
201201-1. Host class is `nixos` or `darwin`
202202-2. At least one user has `"hjem"` in their `classes`
203203-3. `inputs.hjem` exists, or host has a custom `hjem-module` attribute
204204-205205-### den.ctx.hjem-user
206206-207207-| Field | Value |
208208-|-------|-------|
209209-| `provides.hjem-user` | `{ host, user }:` forward hjem class to `hjem.users.<name>` |
139139+ den.ctx.host.into.gpu = { host }:
140140+ lib.optional (host ? gpu) { inherit host; };
141141+}
142142+```
+62-93
docs/src/content/docs/reference/lib.mdx
···11---
22-title: den.lib Reference
33-description: Library functions for parametric dispatch and context handling.
22+title: den.lib
33+description: Library functions for parametric dispatch, argument introspection, and aspect manipulation.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">[`nix/lib.nix`](https://github.com/vic/den/blob/main/nix/lib.nix) · [`nix/fn-can-take.nix`](https://github.com/vic/den/blob/main/nix/fn-can-take.nix) · [`nix/den-brackets.nix`](https://github.com/vic/den/blob/main/nix/den-brackets.nix) · Tests: [`parametric.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/parametric.nix) · [`angle-brackets.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/angle-brackets.nix)</Aside>
88+<Aside title="Source" icon="github">
99+[`nix/lib.nix`](https://github.com/vic/den/blob/main/nix/lib.nix) --
1010+[`nix/fn-can-take.nix`](https://github.com/vic/den/blob/main/nix/fn-can-take.nix)
1111+</Aside>
9121010-## den.lib.parametric
1313+## `den.lib.parametric`
11141212-Creates a parametric aspect. Alias for `(parametric.withOwn parametric.atLeast)`.
1515+Wraps an aspect with a `__functor` that filters `includes` by argument compatibility.
13161417```nix
1515-den.lib.parametric { nixos.x = 1; includes = [ f ]; }
1818+den.lib.parametric { nixos.x = 1; includes = [ ... ]; }
1619```
17201818-Includes owned configs, statics, and dispatches to functions via `atLeast`.
2121+Default uses `atLeast` matching.
2222+2323+### `den.lib.parametric.atLeast`
19242020-### parametric.atLeast
2525+Same as `parametric`. Functions match if all required params are present.
21262222-Dispatches **only** to functions whose required args are a subset of context:
2727+### `den.lib.parametric.exactly`
23282424-**does include owned config, atLeast only forwards context**.
2929+Functions match only if required params exactly equal provided params.
25302631```nix
2727-F = parametric.atLeast { includes = [ a b ]; };
3232+den.lib.parametric.exactly { includes = [ ({ host }: ...) ]; }
2833```
29343030-### parametric.exactly
3535+### `den.lib.parametric.fixedTo`
31363232-Dispatches **only** to functions whose args match context exactly:
3333-3434-**does include owned config, atLeast only forwards context**.
3737+Calls the aspect with a fixed context, ignoring the actual context:
35383639```nix
3737-F = parametric.exactly { includes = [ a b ]; };
4040+den.lib.parametric.fixedTo { host = myHost; } someAspect
3841```
39424040-### parametric.fixedTo
4141-4242-Replaces context with a fixed attribute set:
4343+### `den.lib.parametric.expands`
43444444-Ignores any given context at call site, and replaces with a fixed one.
4545-Behaves like `parametric`, includes owned + static includes + function includes.
4545+Extends the received context with additional attributes before dispatch:
46464747```nix
4848-F = parametric.fixedTo { x = 1; } aspect;
4848+den.lib.parametric.expands { extra = true; } someAspect
4949```
50505151-### parametric.expands
5151+### `den.lib.parametric.withOwn`
52525353-Merges extra attributes into received context:
5454-Behaves like `parametric`, includes owned + static includes + function includes.
5353+Low-level constructor. Takes a `functor: self -> ctx -> aspect` and wraps
5454+an aspect so that owned configs and statics are included at the static
5555+stage, and the functor runs at the parametric stage.
55565656-```nix
5757-F = parametric.expands { extra = 1; } aspect;
5858-```
5757+## `den.lib.canTake`
59586060-### parametric.withOwn
5959+Function argument introspection.
61606262-Combinator: adds owned + statics on top of a dispatch functor:
6363-This is how `parametric` function itself uses `parametric.atLeast`.
6161+### `den.lib.canTake params fn`
64626565-```nix
6666-# an exactly variant of parametric.
6767-F = parametric.withOwn parametric.exactly aspect;
6868-```
6363+Returns `true` if `fn`'s required arguments are satisfied by `params` (atLeast).
69647070-## den.lib.take
6565+### `den.lib.canTake.atLeast params fn`
71667272-Individual function matching:
6767+Same as `canTake`.
73687474-### take.atLeast
6969+### `den.lib.canTake.exactly params fn`
75707676-```nix
7777-den.lib.take.atLeast ({ host, ... }: { nixos.x = 1; })
7878-```
7171+Returns `true` only if `fn`'s required arguments exactly match `params`.
79728080-Wraps function to only be called when context has at least the required args.
7373+## `den.lib.take`
81748282-Produces an empty attrset if the context has not atLeast the expected args.
7575+Conditional function application.
83768484-### take.exactly
7777+### `den.lib.take.atLeast fn ctx`
85788686-```nix
8787-den.lib.take.exactly ({ host }: { nixos.x = 1; })
8888-```
7979+Calls `fn ctx` if `canTake.atLeast ctx fn`, otherwise returns `{}`.
89809090-Only called when context has exactly these args, no more.
8181+### `den.lib.take.exactly fn ctx`
91829292-Produces an empty attrset otherwise.
8383+Calls `fn ctx` if `canTake.exactly ctx fn`, otherwise returns `{}`.
93849494-### take.unused
8585+### `den.lib.take.unused`
95869696-```nix
9797-den.lib.take.unused ignored_value result
9898-```
8787+`_unused: used: used` -- ignores first argument, returns second. Used for
8888+discarding `aspect-chain` in `import-tree`.
9989100100-Returns `result`, ignoring the first argument. Used internally.
9090+## `den.lib.statics`
10191102102-## den.lib.canTake
103103-104104-Function signature introspection:
105105-106106-Does not care about values, only about attribute names.
9292+Extracts only static includes from an aspect (non-function includes):
1079310894```nix
109109-den.lib.canTake { x = 1; } someFunction
110110-# => true if someFunction can take at least { x }
111111-112112-den.lib.canTake.atLeast { x = 1; } someFunction
113113-den.lib.canTake.exactly { x = 1; y = 2; } someFunction
9595+den.lib.statics someAspect { class = "nixos"; aspect-chain = []; }
11496```
11597116116-## den.lib.aspects
9898+## `den.lib.owned`
11799118118-Re-export of [`flake-aspects`](https://github.com/vic/flake-aspects) library. Provides:
119119-- `aspects.types.aspectsType` — module type for aspect trees
120120-- `aspects.types.providerType` — type for aspect providers
121121-- `aspects.forward` — class forwarding implementation
122122-123123-## den.lib.isFn
124124-125125-Checks if a value is callable (function or attrset with `__functor`):
100100+Extracts owned configs from an aspect (removes `includes`, `__functor`):
126101127102```nix
128128-den.lib.isFn someValue # => bool
103103+den.lib.owned someAspect
129104```
130105131131-## den.lib.owned
106106+## `den.lib.isFn`
132107133133-Extracts only owned configs from an aspect (**removes** `includes`, `__functor` from someAspect):
108108+Returns `true` if the value is a function or has `__functor`:
134109135110```nix
136136-den.lib.owned someAspect # => { nixos = ...; darwin = ...; }
111111+den.lib.isFn myValue
137112```
138113139139-## den.lib.statics
114114+## `den.lib.isStatic`
140115141141-Creates a functor that only resolves static includes from an aspect:
142142-143143-The new aspect ignores owned configs, and functional includes that are not
144144-of type (ground flake-aspects): `{ class, aspect-chain }: {...}`
116116+Returns `true` if the function can take `{ class, aspect-chain }`:
145117146118```nix
147147-den.lib.statics someAspect # => <aspect with only static includes>
119119+den.lib.isStatic myFn
148120```
149121150150-## den.lib.isStatic
122122+## `den.lib.__findFile`
151123152152-Checks if a function requires only `{ class, aspect-chain }`:
124124+The angle bracket resolver. See [Angle Brackets Syntax](/guides/angle-brackets/).
153125154126```nix
155155-den.lib.isStatic someFunction # => bool
127127+_module.args.__findFile = den.lib.__findFile;
156128```
157129158158-## den.lib.__findFile
130130+## `den.lib.aspects`
159131160160-Angle-bracket resolver. Translates `<path>` expressions to aspect lookups:
161161-162162-```nix
163163-_module.args.__findFile = den.lib.__findFile;
164164-# then: <foo/bar> => den.aspects.foo.provides.bar
165165-```
132132+The full [flake-aspects](https://github.com/vic/flake-aspects) API,
133133+initialized with the current `lib`. Provides `resolve`, `merge`, type
134134+definitions, and aspect manipulation functions.
+41-63
docs/src/content/docs/reference/output.mdx
···11---
22-title: Configuration Output
33-description: How Den builds and outputs final NixOS, Darwin, and Home-Manager configurations.
22+title: Output
33+description: How Den builds flake outputs from host and home declarations.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside title="Use the Source, Luke" icon="github">[`modules/config.nix`](https://github.com/vic/den/blob/main/modules/config.nix) · [`modules/output.nix`](https://github.com/vic/den/blob/main/modules/output.nix) · Tests: [`default-includes.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/default-includes.nix) · [`home-manager/`](https://github.com/vic/den/tree/main/templates/ci/modules/features/home-manager)</Aside>
88+<Aside title="Source" icon="github">
99+[`modules/config.nix`](https://github.com/vic/den/blob/main/modules/config.nix) --
1010+[`modules/output.nix`](https://github.com/vic/den/blob/main/modules/output.nix)
1111+</Aside>
9121010-## Overview
1313+## Build pipeline
11141212-Den transforms `den.hosts` and `den.homes` declarations into standard Nix
1313-configurations placed on `flake.nixosConfigurations`, `flake.darwinConfigurations`,
1414-and `flake.homeConfigurations`.
1515+Den converts `den.hosts` and `den.homes` declarations into flake outputs
1616+through a unified pipeline:
15171618```mermaid
1719graph LR
1818- H["den.hosts"] -->|"osConfiguration"| NC["flake.nixosConfigurations"]
1919- H -->|"osConfiguration"| DC["flake.darwinConfigurations"]
2020- HM["den.homes"] -->|"homeConfiguration"| HC["flake.homeConfigurations"]
2020+ H[den.hosts] --> B[build]
2121+ M[den.homes] --> B
2222+ B --> F[flake.*Configurations]
2123```
22242323-## The Build Process
2424-2525-For each entry in `den.hosts` and `den.homes`, Den:
2626-2727-1. **Resolves the main module** — applies the context pipeline to produce a
2828- single Nix module containing all class-specific configurations
2929-2. **Calls the instantiate function** — passes the module to the platform builder
3030-3. **Places the result** — puts it at `flake.<intoAttr>`
2525+### Host instantiation
31263232-## OS Configurations
3333-3434-Each host in `den.hosts.<system>.<name>` builds via:
2727+For each host in `den.hosts`, Den calls:
35283629```nix
3730host.instantiate {
···4235}
4336```
44374545-The `mainModule` is the result of applying `den.ctx.host { host }` and
4646-resolving the aspect tree for the host's class.
3838+`host.mainModule` is internally computed by resolving the host's aspect
3939+with its context (`den.ctx.host`), collecting all class-specific config
4040+and dispatched includes.
4141+4242+The result is placed at `flake.<intoAttr>` -- by default
4343+`flake.nixosConfigurations.<name>` or `flake.darwinConfigurations.<name>`.
47444848-## Home Configurations
4545+### Home instantiation
49465050-Each home in `den.homes.<system>.<name>` builds via:
4747+For each home in `den.homes`, Den calls:
51485249```nix
5350home.instantiate {
···5653}
5754```
58555959-Where `home.pkgs` defaults to `inputs.nixpkgs.legacyPackages.<system>`.
5656+The result lands at `flake.homeConfigurations.<name>` by default.
60576161-## Output Placement
5858+## `output.nix` -- flake-parts compatibility
62596363-Results are folded into `flake` by `intoAttr`:
6060+When `inputs.flake-parts` is absent, Den defines its own `options.flake`
6161+option (adapted from flake-parts, MIT licensed) so that output generation
6262+works identically with or without flake-parts.
64636565-| Class | Default `intoAttr` |
6666-|-------|-------------------|
6767-| `nixos` | `nixosConfigurations.<name>` |
6868-| `darwin` | `darwinConfigurations.<name>` |
6969-| `systemManager` | `systemConfigs.<name>` |
7070-| `homeManager` | `homeConfigurations.<name>` |
6464+This means Den can produce `nixosConfigurations`, `darwinConfigurations`,
6565+`homeConfigurations`, and any custom output attribute regardless of
6666+whether flake-parts is loaded.
71677272-Override `intoAttr` to place configs elsewhere:
6868+## Custom output paths
6969+7070+Override `intoAttr` on any host or home to place outputs at a custom path:
73717472```nix
7575-den.homes.x86_64-linux.tux = {
7676- intoAttr = [ "homeConfigurations" "tux@igloo" ];
7373+den.hosts.x86_64-linux.myhost = {
7474+ intoAttr = [ "nixosConfigurations" "custom-name" ];
7775};
7876```
79778080-## Main Module Resolution
7878+## Custom instantiation
81798282-The bridge between Den's schema and final modules is the `mainModule`,
8383-computed internally as:
8080+Override `instantiate` to use a different builder or add `specialArgs`:
84818585-```mermaid
8686-graph TD
8787- Entity["host / home definition"]
8888- Entity -->|"ctx.host / ctx.home"| Intent["Apply context pipeline"]
8989- Intent -->|"aspect.resolve { class }"| Module["Obtain Nix module"]
9090- Module --> Instantiate["instantiate configuration"]
9191- Instantiate --> Output["set value into flake.<intoAttr>"]
8282+```nix
8383+den.hosts.x86_64-linux.myhost = {
8484+ instantiate = inputs.nixos-unstable.lib.nixosSystem;
8585+};
9286```
9393-9494-The context pipeline collects contributions from:
9595-- The entity's own aspect (host aspect or home aspect)
9696-- User aspects (for hosts with users)
9797-- `den.default` includes
9898-- Context-specific includes (`den.ctx.*.includes`)
9999-- Batteries and namespace aspects
100100-101101-All contributions for the matching class are merged into one module.
102102-103103-## Without Flake-Parts
104104-105105-When `inputs.flake-parts` is not present, Den provides a minimal
106106-`options.flake` definition so that configuration outputs have a place
107107-to be collected. This allows Den to work standalone or with any
108108-module evaluation system.
+81-59
docs/src/content/docs/reference/schema.mdx
···11---
22-title: Entity Schema Reference
33-description: Host, user, and home configuration options.
22+title: Schema
33+description: Host, user, and home declaration options, base modules, and freeform types.
44---
5566import { Aside } from '@astrojs/starlight/components';
7788+<Aside title="Source" icon="github">
99+[`modules/options.nix`](https://github.com/vic/den/blob/main/modules/options.nix) --
1010+[`modules/_types.nix`](https://github.com/vic/den/blob/main/modules/_types.nix)
1111+</Aside>
81299-<Aside title="Use the Source, Luke" icon="github">[`modules/_types.nix`](https://github.com/vic/den/blob/main/modules/_types.nix) · [`modules/options.nix`](https://github.com/vic/den/blob/main/modules/options.nix) · Tests: [`host-options.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/host-options.nix) · [`schema-base-modules.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/schema-base-modules.nix) · [`homes.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/homes.nix)</Aside>
1313+## `den.hosts`
10141111-`den.base` provides extensible schemas for `host`/`user`/`home`.
1515+Type: `attrsOf systemType`
12161313-## den.hosts
1717+Keyed by system string (e.g., `"x86_64-linux"`). Each system contains
1818+host definitions as freeform attribute sets.
14191520```nix
1616-den.hosts.<system>.<name> = { ... };
2121+den.hosts.x86_64-linux.myhost = {
2222+ users.vic = {};
2323+};
1724```
18251919-### Host Options
2626+### Host options
20272128| Option | Type | Default | Description |
2229|--------|------|---------|-------------|
2323-| `name` | str | attr name | Host configuration name |
2424-| `hostName` | str | `name` | Network hostname |
2525-| `system` | str | parent key | Platform (e.g., `x86_64-linux`) |
2626-| `class` | str | guessed | `nixos`, `darwin`, or `systemManager` |
2727-| `aspect` | str | `name` | Main aspect name |
2828-| `description` | str | auto | Human-readable description |
2929-| `users` | attrset | `{}` | User definitions |
3030-| `instantiate` | function | auto | Builder function |
3131-| `intoAttr` | str | auto | Flake output attribute |
3232-| `mainModule` | module | auto | Resolved NixOS/Darwin module |
3333-| *freeform* | anything | — | Custom attributes |
3434-3535-### Class Detection
3636-3737-| System suffix | Default class | Default intoAttr |
3838-|--------------|---------------|------------------|
3939-| `*-linux` | `nixos` | `nixosConfigurations` |
4040-| `*-darwin` | `darwin` | `darwinConfigurations` |
3030+| `name` | `str` | attr name | Configuration name |
3131+| `hostName` | `str` | `name` | Network hostname |
3232+| `system` | `str` | parent key | Platform (e.g., `x86_64-linux`) |
3333+| `class` | `str` | auto | `"nixos"` or `"darwin"` based on system |
3434+| `aspect` | `str` | `name` | Main aspect name for this host |
3535+| `description` | `str` | auto | `class.hostName@system` |
3636+| `users` | `attrsOf userType` | `{}` | User accounts on this host |
3737+| `instantiate` | `raw` | auto | OS builder function |
3838+| `intoAttr` | `listOf str` | auto | Flake output path |
3939+| `*` | `den.base.host` options | | Options from base module |
4040+| `*` | | | free-form attributes |
41414242-### Instantiation
4242+### `instantiate` defaults
43434444-| Class | Default function |
4545-|-------|-----------------|
4444+| Class | Default |
4545+|-------|---------|
4646| `nixos` | `inputs.nixpkgs.lib.nixosSystem` |
4747| `darwin` | `inputs.darwin.lib.darwinSystem` |
4848| `systemManager` | `inputs.system-manager.lib.makeSystemConfig` |
49495050-## User Options
5050+### `intoAttr` defaults
5151+5252+| Class | Default |
5353+|-------|---------|
5454+| `nixos` | `[ "nixosConfigurations" name ]` |
5555+| `darwin` | `[ "darwinConfigurations" name ]` |
5656+| `systemManager` | `[ "systemConfigs" name ]` |
51575252-```nix
5353-den.hosts.<system>.<host>.users.<name> = { ... };
5454-```
5858+## `den.hosts.<sys>.<host>.users`
5959+6060+Type: `attrsOf userType`
6161+6262+### User options
55635664| Option | Type | Default | Description |
5765|--------|------|---------|-------------|
5858-| `name` | str | attr name | User configuration name |
5959-| `userName` | str | `name` | OS account name |
6060-| `class` | str | `homeManager` | Home management class |
6161-| `aspect` | str | `name` | Main aspect name |
6262-| *freeform* | anything | — | Custom attributes |
6666+| `name` | `str` | attr name | User configuration name |
6767+| `userName` | `str` | `name` | System account name |
6868+| `classes` | `listOf str` | `[ "homeManager" ]` | Home management classes |
6969+| `aspect` | `str` | `name` | Main aspect name |
7070+| `*` | `den.base.user` options | | Options from base module |
7171+| `*` | | | free-form attributes |
7272+7373+Freeform: additional attributes pass through to the user module.
7474+7575+## `den.homes`
7676+7777+Type: `attrsOf homeSystemType`
63786464-## den.homes
7979+Standalone home-manager configurations, keyed by system string.
65806681```nix
6767-den.homes.<system>.<name> = { ... };
8282+den.homes.x86_64-linux.vic = {};
6883```
69847070-### Home Options
8585+### Home options
71867287| Option | Type | Default | Description |
7388|--------|------|---------|-------------|
7474-| `name` | str | attr name | Home configuration name |
7575-| `userName` | str | `name` | User account name |
7676-| `system` | str | parent key | Platform system |
7777-| `class` | str | `homeManager` | Home management class |
7878-| `aspect` | str | `name` | Main aspect name |
7979-| `description` | str | auto | Human-readable description |
8080-| `pkgs` | attrset | auto | Nixpkgs instance |
8181-| `instantiate` | function | auto | Builder function |
8282-| `intoAttr` | str | `homeConfigurations` | Flake output attribute |
8383-| `mainModule` | module | auto | Resolved HM module |
8484-| *freeform* | anything | — | Custom attributes |
8989+| `name` | `str` | attr name | Home configuration name |
9090+| `userName` | `str` | `name` | User account name |
9191+| `system` | `str` | parent key | Platform system |
9292+| `class` | `str` | `"homeManager"` | Home management class |
9393+| `aspect` | `str` | `name` | Main aspect name |
9494+| `description` | `str` | auto | `home.userName@system` |
9595+| `pkgs` | `raw` | `inputs.nixpkgs.legacyPackages.$sys` | Nixpkgs instance |
9696+| `instantiate` | `raw` | `inputs.home-manager.lib.homeManagerConfiguration` | Builder |
9797+| `intoAttr` | `listOf str` | `[ "homeConfigurations" name ]` | Output path |
9898+| `*` | `den.base.home` options | | Options from base module |
9999+| `*` | | | free-form attributes |
100100+101101+## `den.base`
851028686-## den.base
103103+Base modules merged into all hosts, users, or homes.
871048888-Base modules applied to all entities of a type:
105105+| Option | Type | Description |
106106+|--------|------|-------------|
107107+| `den.base.conf` | `deferredModule` | Applied to host, user, and home |
108108+| `den.base.host` | `deferredModule` | Applied to all hosts (imports `conf`) |
109109+| `den.base.user` | `deferredModule` | Applied to all users (imports `conf`) |
110110+| `den.base.home` | `deferredModule` | Applied to all homes (imports `conf`) |
8911190112```nix
9191-den.base.conf = { ... }: { }; # hosts + users + homes
9292-den.base.host = { host, ... }: { };
9393-den.base.user = { user, ... }: { };
9494-den.base.home = { home, ... }: { };
113113+den.base.conf = { lib, ... }: {
114114+ # shared across all host/user/home declarations
115115+};
116116+den.base.host = { ... }: {
117117+ # host-specific base config
118118+};
95119```
9696-9797-`den.base.conf` is automatically imported by host, user, and home.
-9
docs/src/content/docs/sponsor.md
···11----
22-title: Sponsor Den development
33-description: Become an sponsor and help Den development
44----
55-66-77-Den is made with love by [vic](https://bsky.app/profile/oeiuwq.bsky.social).
88-99-If you find Den useful, consider [sponsoring](https://github.com/sponsors/vic).
+1-1
docs/src/content/docs/tutorials/noflake.md
···126126127127## Next Steps
128128129129-- Read [Use Without Flakes](/guides/no-flakes/) for more details on flake-free usage
129129+- Read [Core Principles](/explanation/core-principles/) to understand how Den works without flakes
130130- Consider the [Minimal template](/tutorials/minimal/) if you want flakes but not flake-parts
···43434444in
4545{
4646- den.provides.home-manager = { };
4646+ den.provides.home-manager =
4747+ _:
4848+ throw ''
4949+ NOTICE: den.provides.home-manager aspect is not used anymore.
5050+ See https://den.oeiuwq.com/guides/home-manager/
5151+5252+ Home Manager is now enabled via host config:
5353+5454+ # den.ctx.hm-host requires least one user with homeManager class.
5555+ den.hosts.x86_64-linux.igloo.users.tux.classes = [ "homeManager" ];
5656+5757+ Globally:
5858+5959+ den.base.user.classes = [ "homeManager" ];
6060+6161+ See <den/home-manager/hm-os.nix>
6262+6363+ If you had includes at den._.home-manager, you can use:
6464+6565+ den.ctx.hm-host.includes = [ ... ];
6666+6767+ For attaching aspects to home-manager enabled hosts.
6868+ '';
47694870 den.ctx.home.description = "Standalone Home-Manager config provided by home aspect";
4971 den.ctx.home._.home = { home }: parametric.fixedTo { inherit home; } den.aspects.${home.aspect};
+4-1
templates/bogus/modules/bug.nix
···1212 }:
1313 {
1414 # replace <system> if you are reporting a bug in MacOS
1515- den.hosts.x86_64-linux.igloo.users.tux = { };
1515+ den.hosts.x86_64-linux.igloo.users.tux.classes = [
1616+ "user"
1717+ "homeManager"
1818+ ];
16191720 # do something for testing
1821 den.aspects.tux.user.description = "The Penguin";
+1
templates/ci/modules/features/user-classes.nix
···22222323 expr = lib.sort (a: b: a < b) den.hosts.x86_64-linux.igloo.users.tux.classes;
2424 expected = [
2525+ "homeManager" # this one comes from globally enabled at test support.
2526 "homeManager"
2627 "maid"
2728 ];