···77assignees: ''
8899---
1010+Please read https://den.oeiuwq.com/tutorials/bogus/ first.
10111112If you have found a bug, please share a reproduction repository with us.
1213···2021```
21222223Your repository will help us verify that we can reproduce the bug in a minimal environment - Your repo has CI actions enabled. When the bug has been fixed we can use your same code as a non-regression test to ensure bugs do not appear again.
2424+2525+If you found a bug regression please edit the CI workflow to include main and any other revision to test and show something was working before main.
23262427### Description
2528
+1-1
README.md
···2727- [Dendritic](https://den.oeiuwq.com/explanation/core-principles/): **same** concern, **different** Nix classes.
2828- [Flake optional](https://den.oeiuwq.com/guides/no-flakes/). Stable/unstable Nix, with/without flake-parts.
2929- [DRY](modules/aspects/provides/unfree/unfree.nix) & [`class`-generic](modules/aspects/provides/primary-user.nix) — [parametric](https://den.oeiuwq.com/explanation/parametric/) over `host`/`home`/`user`.
3030-- Context-aware [dependencies](https://den.oeiuwq.com/explanation/context-system/); `host<->user` [bidirectional](https://den.oeiuwq.com/guides/bidirectional/) contributions.
3030+- Context-aware [dependencies](https://den.oeiuwq.com/explanation/context-system/); `host<->user` [bidirectional](https://den.oeiuwq.com/guides/configure-aspects/) contributions.
3131- [Share](https://den.oeiuwq.com/guides/namespaces/) aspects across repos. [Routable](templates/example/modules/aspects/eg/routes.nix) configs.
3232- Custom [factories](https://github.com/vic/den/blob/f5c44098e4855e07bf5cbcec00509e75ddde4220/templates/ci/modules/homes.nix#L20) for any Nix `class`. Per-host `stable`/`unstable` channels.
3333- Freeform [schemas](https://den.oeiuwq.com/reference/schema/) (no `specialArgs`) with [base](https://github.com/vic/den/pull/119) modules.
···33description: How aspects use the __functor pattern for context-awareness.
44---
5566+import { Aside } from '@astrojs/starlight/components';
77+6877-> Use the source, Luke: [`nix/lib.nix`](https://github.com/vic/den/blob/main/nix/lib.nix) · Built on [flake-aspects](https://github.com/vic/flake-aspects)
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)
1111+</Aside>
812913## The __functor Pattern
1014
···11----
22-title: OS Context Pipeline
33-description: The complete data flow from host declaration to final configuration.
44----
55-66-> Use the source, Luke: [`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)
77-88-## The NixOS/nix-Darwin Pipeline
99-1010-When Den evaluates a host configuration, data flows through a pipeline
1111-of context transformations. Here is the complete picture:
1212-1313-```mermaid
1414-graph TD
1515- Host["den.hosts.x86_64-linux.igloo"]
1616- Host -->|"initial context"| CtxHost["ctx.host { host }"]
1717-1818- CtxHost -->|"conf"| HA["den.aspects.igloo<br/>(fixedTo { host })"]
1919- CtxHost -->|"into.default"| CtxDef1["<b>ctx.default</b> { host }"]
2020- CtxHost -->|"into.user<br/>(per user)"| CtxUser["ctx.user { host, user }"]
2121- CtxHost -->|"into.hm-host<br/>(if HM detected)"| CtxHM["ctx.hm-host { host }"]
2222-2323- CtxUser -->|"conf"| UA["den.aspects.tux<br/>(fixedTo { host, user })"]
2424- CtxUser -->|"into.default"| CtxDef2["<b>ctx.default</b> { host, user }"]
2525-2626- CtxHM -->|"conf"| HMmod["Import HM module"]
2727- CtxHM -->|"into.hm-user<br/>(per HM user)"| CtxHMU["ctx.hm-user { host, user }"]
2828-2929- CtxHMU -->|"forward homeManager<br/>into host"| FW["home-manager.users.tux"]
3030-3131- CtxDef1 -->|"includes"| DI1["<b>den.default.{class}</b> ()<br/>den.default.includes<br/>(host-context funcs)"]
3232- CtxDef2 -->|"includes"| DI2["den.default.includes<br/>(user-context funcs)"]
3333-```
3434-3535-## Stage by Stage
3636-3737-### 1. Host Entry
3838-3939-Den reads `den.hosts.x86_64-linux.igloo` and applies the initial context `den.ctx.host`:
4040-4141-```nix
4242-den.ctx.host { host = den.hosts.x86_64-linux.igloo; }
4343-```
4444-4545-### 2. Host Aspect Resolution
4646-4747-`den.ctx.host.conf` locates `den.aspects.igloo` and fixes it to the host context.
4848-All owned configs and static includes from the host aspect are collected.
4949-5050-### 3. Default configs (host-level)
5151-5252-`den.ctx.host.into.default` produces `{ host }` for `den.ctx.default`, which
5353-activates `den.default.includes` functions matching `{ host, ... }`.
5454-5555-### 4. User Enumeration
5656-5757-`den.ctx.host.into.user` maps over `host.users`, producing one
5858-`den.ctx.user { host, user }` per user.
5959-6060-### 5. User Aspect Resolution
6161-6262-`den.ctx.user.conf` locates both the user's aspect (`den.aspects.tux`) and the
6363-host's aspect, collecting contributions from both directions.
6464-6565-### 6. Default configs (user-level)
6666-6767-`ctx.user.into.default` activates `den.default.includes` again, this time
6868-with `{ host, user }` — functions needing user context now match.
6969-7070-### 7. Home-Manager Detection
7171-7272-`den.ctx.host.into.hm-host` checks if the host has users with `homeManager`
7373-class and a supported OS. If so, it activates `den.ctx.hm-host`.
7474-7575-### 8. HM Module Import (host-level)
7676-7777-`den.ctx.hm-host.conf` imports the Home-Manager NixOS/Darwin module.
7878-7979-### 9. HM User config collection (user-level)
8080-8181-For each HM user, `ctx.hm-user` uses `den._.forward` to take
8282-`homeManager` class configs and insert them into
8383-`home-manager.users.<name>` on the host.
8484-8585-## Home-Manager Detection Criteria
8686-8787-`ctx.host.into.hm-host` does not always activate. It checks three conditions
8888-(see [`hm-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-os.nix)):
8989-9090-1. **OS class is supported** — the host's class is `nixos` or `darwin`
9191-2. **HM users exist** — at least one user has `class = "homeManager"`
9292-3. **HM module available** — `inputs.home-manager` exists, or the host has a custom `hm-module`
9393-9494-All three must be true. Hosts without users, or with only non-HM users,
9595-skip the entire HM pipeline.
9696-9797-## Duplication Caveat with den.default
9898-9999-`den.default` (alias for `den.ctx.default`) is included at **{host}**, **{host, user}**, **{home}**
100100-context stages — once for the host context and once __per each__ user.
101101-102102-This means:
103103-104104-- **Owned** configs and **static** includes from `den.default` can appear
105105- multiple times in the final configuration
106106-- For `mkMerge`-compatible options (most NixOS options), this is harmless
107107-- For options of **types.listOf** or **types.package**, you may get duplicate entries.
108108-109109-To avoid duplication, use `den.lib.take.exactly` to restrict which
110110-context stages a function matches:
111111-112112-```nix
113113-den.default.includes = [
114114- (den.lib.take.exactly ({ host }: { nixos.x = 1; }))
115115-];
116116-```
117117-118118-This function runs only in the `{ host }` context, not in `{ host, user }`.
119119-120120-:::tip
121121-Prefer attaching configurations directly to specific host or user aspects, or use
122122-`den.ctx.host` / `den.ctx.user` includes, rather than overloading `den.default`
123123-for everything.
124124-:::
125125-126126-## Standalone Home-Manager
127127-128128-For `den.ctx.home`, the pipeline is shorter:
129129-130130-```mermaid
131131-graph TD
132132- Home["den.homes.x86_64-linux.tux"]
133133- Home -->|"creates"| CtxHome["ctx.home { home }"]
134134- CtxHome -->|"conf"| HomeA["den.aspects.tux<br/>(fixedTo { home })"]
135135- CtxHome -->|"into.default"| CtxDef["ctx.default { home }"]
136136-```
137137-138138-## den.default Is an Alias
139139-140140-`den.default` is an alias for `den.ctx.default`. When you write:
141141-142142-```nix
143143-den.default.homeManager.home.stateVersion = "25.11";
144144-den.default.includes = [ den._.define-user ];
145145-```
146146-147147-You are actually setting `den.ctx.default.homeManager...` and
148148-`den.ctx.default.includes`. This means `den.default` is a full
149149-context type — it has `conf`, `into`, `includes`, and owned attributes.
150150-151151-### How den.default Receives Data
152152-153153-`host`, `user`, and `home` aspects **do not** include `den.default` directly.
154154-Instead, each context type transforms **into** `default`:
155155-156156-```nix
157157-den.ctx.host.into.default = lib.singleton; # passes { host }
158158-den.ctx.user.into.default = lib.singleton; # passes { host, user }
159159-den.ctx.home.into.default = lib.singleton; # passes { home }
160160-```
161161-162162-This means `den.default` is reached through the declarative context
163163-pipeline, not by direct inclusion. The data flowing into `den.default`
164164-is whatever the source context provides.
165165-166166-### Best Practices
167167-168168-`den.default` is useful for global settings like `home.stateVersion`.
169169-However, prefer attaching parametric includes to the appropriate
170170-context type instead:
171171-172172-| Instead of | Use |
173173-|-----------|-----|
174174-| `den.default.includes = [ hostFunc ]` | `den.ctx.host.includes = [ hostFunc ]` |
175175-| `den.default.includes = [ hmFunc ]` | `den.ctx.hm-host.includes = [ hmFunc ]` |
176176-| `den.default.nixos.x = 1` | `den.ctx.host.nixos.x = 1` |
177177-178178-## Why This Design?
179179-180180-Each context type is **independent** and **composable**. You can:
181181-182182-- Add new context types without modifying existing ones
183183-- Attach aspects to any stage of the pipeline
184184-- Create custom transformations for domain-specific needs
185185-- Override any built-in context behavior
186186-187187-The pipeline is not hardcoded — it's declared through `den.ctx` definitions
188188-that you can inspect, extend, and customize.
···11+---
22+title: OS Context Pipeline
33+description: The complete data flow from host declaration to final configuration.
44+---
55+66+77+import { Aside } from '@astrojs/starlight/components';
88+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) · 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)
1111+</Aside>
1212+1313+## The NixOS/nix-Darwin Pipeline
1414+1515+When Den evaluates a host configuration, data flows through a pipeline
1616+of context transformations. Here is the complete picture:
1717+1818+```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)"]
3939+```
4040+4141+## Stage by Stage
4242+4343+### 1. Host Entry
4444+4545+Den reads `den.hosts.x86_64-linux.igloo` and applies the initial context `den.ctx.host`:
4646+4747+```nix
4848+den.ctx.host { host = den.hosts.x86_64-linux.igloo; }
4949+```
5050+5151+### 2. Host Aspect Resolution
5252+5353+`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.
5656+5757+### 3. Default configs (host-level)
5858+5959+`den.ctx.host.into.default` produces `{ host }` for `den.ctx.default`, which
6060+activates `den.default.includes` functions matching `{ host, ... }`.
6161+6262+### 4. User Enumeration
6363+6464+`den.ctx.host.into.user` maps over `host.users`, producing one
6565+`den.ctx.user { host, user }` per user.
6666+6767+### 5. User Aspect Resolution
6868+6969+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 }`
7272+7373+### 6. Default configs (user-level)
7474+7575+`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**).
7979+8080+### 7. Home-Manager Detection
8181+8282+`den.ctx.host.into.hm-host` checks if the host has users with `homeManager`
8383+class and a supported OS. If so, it activates `den.ctx.hm-host`.
8484+8585+### 8. HM Module Import (host-level)
8686+8787+`den.ctx.hm-host.provides.hm-host` imports the Home-Manager NixOS/Darwin module.
8888+8989+### 9. HM User config collection (user-level)
9090+9191+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 `class = "homeManager"`
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:
161161+162162+```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.
···2929(hosts, users, homes) and context types (shapes holding them). Den transforms these through
3030a graph of context stages, each producing richer or conditioned data.
31313232-The key insight: **the flow of data is declared separately from the
3333-configurations it produces**.
3232+**the flow of data is declared separately from the configurations it produces**.
34333534### 2. Context-Aware Aspects
3635···3938adapts to every host-user combination. A function requiring `{ other }` is
4039not used.
41404242-The key insight: **the shape of the function arguments determines when
4343-configs are produced, without explicit conditionals**.
4141+**the shape of the function arguments determines when configs are produced, without explicit conditionals**.
44424543## Why This Matters
4644
···33description: Understanding Den's dual nature as both a library and a framework.
44---
5566+67## Den the Library
7889At its core, Den is a **library** for activating configuration aspects via context transformations.
···4546Here is a deployment pipeline example:
46474748```nix
4848-den.ctx.deploy.conf = { target }:
4949+den.ctx.deploy._.deploy = { target }:
4950 den.aspects.${target.name};
50515152den.ctx.deploy.into.service = { target }:
5253 map (s: { service = s; target = target; })
5354 target.services;
54555555-den.ctx.service.conf = { service, target }:
5656+den.ctx.service._.service = { service, target }:
5657 { terraform.resource.${service.name} = { ... }; };
5758```
5859···61622. Targets enumerate their services
62633. Each service produces Terraform configurations
63646464-The same `ctxApply` mechanics — owned configs, `conf` lookup,
6565+The same `ctxApply` mechanics — owned configs, self-named provider lookup,
6566`into` transformations — drive this pipeline without any OS-specific code.
66676768You can even design and test custom context flows independently of
···33description: How parametric functors enable context forwarding and adaptation.
44---
5566-> Use the source, Luke: [`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)
66+import { Aside } from '@astrojs/starlight/components';
77+88+<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)
1010+</Aside>
711812## What Is a Parametric Aspect?
913···3539Den provides several parametric functors in
3640`den.lib.parametric`. Each of them provides a
3741different `__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'
5252+```
5353+5454+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>
5757+38583959## den.lib.parametric
4060···176196177197## take.exactly and take.atLeast
178198179179-For individual functions (not whole aspects), use [`den.lib.take`](/reference/lib/#denlibatake):
199199+For individual functions (not whole aspects), use [`den.lib.take`](/reference/lib/#denlibtake):
180200181201182202```nix
···5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside type="tip">Source: [`modules/aspects/provides/`](https://github.com/vic/den/tree/main/modules/aspects/provides) · Reference: [Batteries](/reference/batteries/)</Aside>
88+<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/)
1010+</Aside>
9111012## What Are Batteries?
1113···109111```
110112111113This is how Home-Manager integration is implemented internally.
112112-113113-## Writing Your Own
114114-115115-Any aspect can serve as a battery. Publish it in a namespace for others:
116116-117117-```nix
118118-{ den, ... }: {
119119- den.provides.my-battery = den.lib.parametric {
120120- includes = [
121121- ({ host, ... }: { nixos.my-setting = host.name; })
122122- ];
123123- };
124124-}
125125-```
-95
docs/src/content/docs/guides/bidirectional.md
···11----
22-title: Bidirectional Dependencies
33-description: Hosts configure users and users configure hosts — automatically.
44----
55-66-## Host → User Configuration
77-88-A host aspect's `homeManager` settings apply to **all users** on that host:
99-1010-```nix
1111-den.aspects.igloo.homeManager.programs.direnv.enable = true;
1212-```
1313-1414-If `igloo` has users `tux` and `pingu`, both get direnv enabled.
1515-1616-This also works with static includes:
1717-1818-```nix
1919-den.aspects.igloo.includes = [
2020- { homeManager.programs.direnv.enable = true; }
2121-];
2222-```
2323-2424-And with parametric includes that receive context:
2525-2626-```nix
2727-den.aspects.igloo.includes = [
2828- ({ host, user }: {
2929- homeManager.programs.direnv.enable = true;
3030- })
3131-];
3232-```
3333-3434-## User → Host Configuration
3535-3636-A user aspect's `nixos` or `darwin` settings apply to **every host** with that user:
3737-3838-```nix
3939-den.aspects.tux.nixos.programs.fish.enable = true;
4040-```
4141-4242-If `tux` is on both `igloo` and `iceberg`, both hosts get fish enabled.
4343-4444-User includes work the same way:
4545-4646-```nix
4747-den.aspects.tux.includes = [
4848- ({ host, ... }: {
4949- nixos.users.users.tux.description = "Tux on ${host.name}";
5050- })
5151-];
5252-```
5353-5454-## Context-Conditional Bidirectional
5555-5656-Combine host and user context for precise control:
5757-5858-```nix
5959-let
6060- git-on-linux = { user, host, ... }:
6161- if !lib.hasSuffix "darwin" host.system
6262- then { homeManager.programs.git.enable = true; }
6363- else { };
6464-in {
6565- den.aspects.tux.includes = [ git-on-linux ];
6666-}
6767-```
6868-6969-User `tux` gets git only on Linux hosts, not on Darwin.
7070-7171-## How It Works
7272-7373-```mermaid
7474-graph TD
7575- HA["den.aspects.igloo (host)"] -->|"homeManager class"| U1["tux Home-Manager"]
7676- HA -->|"homeManager class"| U2["pingu Home-Manager"]
7777- UA["den.aspects.tux (user)"] -->|"nixos class"| H1["igloo NixOS"]
7878- UA -->|"nixos class"| H2["iceberg NixOS"]
7979-```
8080-8181-Den’s [context system](/explanation/context-system/) handles the routing:
8282-- Host aspect configs flow to all users on that host
8383-- User aspect configs flow to all hosts with that user
8484-- Context functions get called with the specific `{ host, user }` pair
8585-8686-## The `den.default` Backbone
8787-8888-Settings in [`den.default`](/explanation/context-pipeline/#dendefault-is-an-alias) apply to **everything** — all hosts and all users:
8989-9090-```nix
9191-den.default.homeManager.home.stateVersion = "25.11";
9292-den.default.includes = [
9393- ({ host, ... }: { ${host.class}.networking.hostName = host.name; })
9494-];
9595-```
···5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside type="tip">Source: [`modules/aspects/provides/forward.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/forward.nix) · [CI tests](https://github.com/vic/den/blob/main/templates/ci/modules/forward.nix)</Aside>
88+<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) · [CI tests](https://github.com/vic/den/blob/main/templates/ci/modules/forward.nix)</Aside>
991010## What Are Custom Classes?
1111···20202121## Example: Custom Class to NixOS
22222323-Forward a `custom` class into NixOS top-level:
2323+Forward a new `hjem` class into NixOS top-level:
24242525```nix
2626{ den, lib, ... }:
2727let
2828- forwarded = { class, aspect-chain }:
2828+ hjem = { host }: { class, aspect-chain }:
2929 den._.forward {
3030- each = lib.singleton class;
3131- fromClass = _: "custom";
3232- intoClass = _: "nixos";
3333- intoPath = _: [ ];
3434- fromAspect = _: lib.head aspect-chain;
3030+ each = host.users;
3131+ fromClass = user: "hjem";
3232+ intoClass = user: class;
3333+ intoPath = user: [ "hjem" "users" user.userName ]
3434+ fromAspect = user: den.aspects.${user.aspect};
3535 };
3636in {
3737- den.aspects.igloo = {
3838- includes = [ forwarded ];
3939- custom.networking.hostName = "from-custom-class";
4040- };
3737+ # host forwards user hjem modules into nixos level.
3838+ den.aspects.igloo = { includes = [ hjem ]; };
3939+4040+ # user aspect has hjem class
4141+ den.aspects.tux.hjem = { ... };
4142}
4243```
43444444-## Example: Forward into a Subpath
4545-4646-Insert configs into a nested submodule:
4747-4848-```nix
4949-den.aspects.igloo = {
5050- includes = [ forwarded ];
5151- nixos.imports = [
5252- { options.fwd-box = lib.mkOption {
5353- type = lib.types.submoduleWith { modules = [ myModule ]; };
5454- }; }
5555- ];
5656- src.items = [ "from-src-class" ];
5757-};
5858-```
5959-6060-With `intoPath = _: [ "fwd-box" ]`, the `src` class configs merge into
6161-`nixos.fwd-box`.
62456346## How Home-Manager Uses Forward
6447
+4-11
docs/src/content/docs/guides/debug.md
···2525 });
2626```
27272828-## Trace Context Keys
2929-3030-See which contexts are being applied:
3131-3232-```nix
3333-den.default.includes = [
3434- (context: builtins.trace (builtins.attrNames context) { })
3535-];
3636-```
37283829## REPL Inspection
3930···89809081## Common Issues
91829292-**Duplicate values in lists**: Your function matches too many contexts.
9393-Use `den.lib.take.exactly` to restrict matching:
8383+**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:
94879588```nix
9689den.lib.take.exactly ({ host }: { nixos.x = 1; })
···5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside type="tip">Source: [`nix/namespace.nix`](https://github.com/vic/den/blob/main/nix/namespace.nix)</Aside>
88+<Aside title="Use the Source, Luke" icon="github">[`nix/namespace.nix`](https://github.com/vic/den/blob/main/nix/namespace.nix)</Aside>
991010## What Are Namespaces?
11111212Namespaces let you organize aspects into named collections that can be
1313shared across flakes and consumed by others.
14141515+This is unlike your `den.aspects.<name>` namespace which is private,
1616+because you wont likely want to share your user/host configurations.
1717+1818+Namespaces are for re-usable aspects you are willing to share with others.
1919+1520## Define a Local Namespace
16211722```nix
···2126}
2227```
23282929+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.
3232+2433The second argument controls output:
2534- `false` — local only, not exposed as flake output
2635- `true` — exposed at `flake.denful.ns`
···28372938## Consume Remote Namespaces
30393131-Import aspects from another flake's `denful` output:
4040+Import aspects from another flake's `flake.denful.provider` output:
32413342```nix
3443{ inputs, ... }: {
···4049}
4150```
42514343-Multiple sources are merged. Local definitions override remote ones.
5252+Multiple sources are merged by the module system.
44534554## Nested Provides in Namespaces
4655···92101angle brackets:
9310294103```nix
9595-{ __findFile, ns, ... }: {
104104+{ __findFile, ... }: {
96105 _module.args.__findFile = den.lib.__findFile;
106106+97107 den.aspects.igloo.includes = [ <ns/tools> ];
98108}
99109```
100110101111## Real-World: denful
102112103103-[denful](https://github.com/vic/denful) is a community aspect distribution
113113+[denful](https://github.com/vic/denful) is a (WIP) community aspect distribution
104114built on Den namespaces — a lazyvim-like approach to Nix configurations.
-91
docs/src/content/docs/guides/no-flakes.md
···11----
22-title: Use Without Flakes
33-description: Den works with stable Nix, npins, and without flake-parts.
44----
55-66-## Den is Flake-Agnostic
77-88-Den does **not** require Nix flakes. It works with:
99-1010-- Nix flakes + flake-parts (most common)
1111-- Nix flakes without flake-parts
1212-- No flakes at all (stable Nix + npins or fetchTarball)
1313-1414-## Without Flakes
1515-1616-Use `flake-compat` to evaluate Den from a non-flake setup:
1717-1818-```nix
1919-# default.nix
2020-let
2121- flake = import (fetchTarball {
2222- url = "https://github.com/edolstra/flake-compat/archive/...tar.gz";
2323- }) { src = ./.; };
2424-in
2525- flake.outputs
2626-```
2727-2828-See the [`noflake` template](https://github.com/vic/den/tree/main/templates/noflake)
2929-for a complete working example with npins.
3030-3131-## Without Flake-Parts
3232-3333-Den provides its own minimal `flake` option when flake-parts is not present.
3434-Simply import `inputs.den.flakeModule`:
3535-3636-```nix
3737-let
3838- denCfg = (lib.evalModules {
3939- modules = [
4040- (import-tree ./modules)
4141- inputs.den.flakeModule
4242- ];
4343- specialArgs = { inherit inputs; };
4444- }).config;
4545-in {
4646- nixosConfigurations.igloo =
4747- denCfg.flake.nixosConfigurations.igloo;
4848-}
4949-```
5050-5151-## With Flake-Parts
5252-5353-The standard setup — Den integrates seamlessly:
5454-5555-```nix
5656-{
5757- outputs = inputs:
5858- inputs.flake-parts.lib.mkFlake { inherit inputs; }
5959- (inputs.import-tree ./modules);
6060-6161- inputs = {
6262- den.url = "github:vic/den";
6363- flake-aspects.url = "github:vic/flake-aspects";
6464- import-tree.url = "github:vic/import-tree";
6565- nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
6666- flake-parts.url = "github:hercules-ci/flake-parts";
6767- };
6868-}
6969-```
7070-7171-## The Dendritic Module
7272-7373-For flake-parts users who want automatic Den + flake-aspects setup:
7474-7575-```nix
7676-imports = [ inputs.den.flakeModules.dendritic ];
7777-```
7878-7979-This auto-configures `flake-file` inputs for `den` and `flake-aspects`.
8080-8181-## Minimal Dependencies
8282-8383-Den's only hard dependency is `flake-aspects`. Everything else is optional:
8484-8585-| Dependency | Required? | Purpose |
8686-|-----------|-----------|---------|
8787-| `flake-aspects` | **Yes** | [Aspect composition](https://github.com/vic/flake-aspects) |
8888-| `import-tree` | Optional | [Auto-import module directories](https://github.com/vic/import-tree) |
8989-| `flake-parts` | Optional | Flake structuring |
9090-| `nixpkgs` | Optional | Only if building NixOS/HM configs |
9191-| `home-manager` | Optional | Only if using HM integration |
+3-3
docs/src/content/docs/index.mdx
···99 <img width="400" height="400" src="https://github.com/user-attachments/assets/af9c9bca-ab8b-4682-8678-31a70d510bbb" />
1010 actions:
1111 - text: NixOS Framework
1212- link: /tutorials/getting-started/
1212+ link: /tutorials/overview/
1313 icon: right-arrow
1414 - text: Pure Nix Library
1515 link: /explanation/library-vs-framework
···137137 # generic config for all hosts, users, homes.
138138 den.default.nixos.system.stateVersion = "25.11";
139139 den.default.homeManager.home.stateVersion = "25.11";
140140- den.default.includes [ den._.inputs' ];
140140+ den.default.includes = [ den._.inputs' ];
141141142142}
143143```
···150150 </Card>
151151 <Card title="Bidirectional" icon="right-arrow">
152152 Hosts configure their users. Users contribute to their
153153- hosts. [Aspects flow in both directions](/guides/bidirectional/) automatically.
153153+ hosts. [Aspects flow in both directions](/guides/configure-aspects/) automatically.
154154 </Card>
155155 <Card title="Batteries Included" icon="add-document">
156156 Opt-in [aspects for common tasks](/guides/batteries/): define-user, primary-user,
···33description: The motivation and fundamental idea behind Den.
44---
5566+import { Aside } from '@astrojs/starlight/components';
6777-> This section is about how and why Den came to be.
88-> You can safely skip this unless you are interested in how Den was shaped.
99->
1010-> However you might need to already be familiar with what [Dendritic](https://github.com/mightyiam/dendritic) means
1111-> you might also want to read the [FAQ](https://github.com/Doc-Steve/dendritic-design-with-flake-parts/wiki/FAQ)
88+<Aside>
99+This section is about how and why Den came to be.
1010+You can safely skip this unless you are interested in how Den was shaped.
1111+1212+However you might need to already be familiar with what [Dendritic](https://github.com/mightyiam/dendritic) means
1313+you might also want to read the [FAQ](https://github.com/Doc-Steve/dendritic-design-with-flake-parts/wiki/FAQ)
1414+</Aside>
12151316Den is basically about a single idea:
14171515-> Being able to share re-usable (parametric) cross-class Nix configurations.
1818+<Aside type="tip" icon="star" title="Motivation">
1919+Den was built for being able to share re-usable (parametric) cross-class Nix configurations.
2020+</Aside>
16211722Den was born not to be yet-another zillionth way to wire-up configurations.
1823···3237At that time [unify](https://codeberg.org/quasigod/unify) - the first dendritic framework I know of- entered my radar, and I really loved it! It was missing a couple of features that I needed in my existing infra, but I loved the `<aspect>.<class>` transposition that its author invented, and the cleaness of the code at the author's infra was a testament to how good-looking and organized a Dendritic setup could look. I certainly wanted that for mine.
33383439However, before adopting `unify`, I realized that my re-usability problem was not about syntax, not about how things are loaded from filesystem nor how they are wired at the flake level.
3535-3636-> The problem was more about feature composability.
4040+**The problem was more about feature composability.**
37413842Being an fan of functional programming, the most composable things I know are functions.
3943···5963An important point is that parametric aspect's context **are not** _module.args, they do not depend on config. This allows using them for [conditional](https://github.com/vic/den/blob/main/templates/ci/modules/features/conditional-config.nix) imports.
606461656262-> Some [people](https://codeberg.org/FrdrCkII/nixconf/src/branch/main/modules/aspects.prt.nix) have found `flake-aspects` enough to implement their own Dendritic setups, or even for mixing with other non-dendritic infra.
6666+<Aside>
6767+Some [people](https://codeberg.org/FrdrCkII/nixconf/src/branch/main/modules/aspects.prt.nix) have found [`flake-aspects`](https://github.com/vic/flake-aspects) enough to implement their own Dendritic setups, or even for mixing with other non-dendritic infra.
6868+</Aside>
63696470## Den builds upon flake-aspects
6571···77837884 `den.default.includes = [ (den._.unfree ["vscode"]) ]`
79858080-- How a `Host` can affect its `User`s configurations, and [viceversa](/guides/bidirectional).
8686+- How a `Host` can affect its `User`s configurations, and [viceversa](/guides/configure-aspects).
81878288- How to [mixin](/guides/namespaces) aspects from remote sources and enhance locally defined ones.
8389···9810499105Den is more about giving to others (creating useful Nix software/configurations), not just about make things work locally.
100106101101-> I've been working over a year on these [dendritic libs](https://vic.github.io/dendrix/Dendritic-Ecosystem.html#vics-dendritic-libraries), but time is our most scarce resource. If you like my work, consider [sponsoring](https://github.com/sponsors/vic)
107107+<Aside type="tip" title="Support Den development" icon="heart">
108108+I've been working over a year on these [dendritic libs](https://vic.github.io/dendrix/Dendritic-Ecosystem.html#vics-dendritic-libraries), but time is our most scarce resource. If you like my work, consider [sponsoring](https://github.com/sponsors/vic)
109109+</Aside>
102110103111104112## What's Next?
105113106106-Ready to try it? Head to the [Getting Started](/tutorials/getting-started/) tutorial.
114114+Ready to try it? Head to the [Templates overview](/tutorials/overview/) to pick a starting point.
107115108116Want to understand the architecture? Read about the [Core Principles](/explanation/core-principles/).
-127
docs/src/content/docs/reference/aspects.md
···11----
22-title: den.aspects Reference
33-description: Aspect structure, resolution, and configuration.
44----
55-66-import { Aside } from '@astrojs/starlight/components';
77-88-<Aside type="tip">Source: [`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)</Aside>
99-1010-## Aspect Attributes
1111-1212-Every aspect is an attribute set with these recognized keys:
1313-1414-| Attribute | Type | Description |
1515-|-----------|------|-------------|
1616-| `nixos` | module | NixOS configuration module |
1717-| `darwin` | module | nix-darwin configuration module |
1818-| `homeManager` | module | Home-Manager configuration module |
1919-| `<class>` | module | Any custom Nix class |
2020-| `includes` | list | Dependencies on other aspects or functions |
2121-| `provides` | attrset | Nested sub-aspects |
2222-| `_` | alias | Shorthand for `provides` |
2323-| `description` | str | Human-readable description |
2424-| `__functor` | function | Context-aware behavior |
2525-2626-## Automatic Aspect Creation
2727-2828-Den creates an aspect for each host, user, and home you declare:
2929-3030-```nix
3131-den.hosts.x86_64-linux.igloo.users.tux = { };
3232-# Creates: den.aspects.igloo and den.aspects.tux
3333-```
3434-3535-Aspects are created with `parametric { <class> = {}; }` functor
3636-matching the entity's class.
3737-3838-## Aspect Resolution
3939-4040-To extract a class module from an aspect:
4141-4242-```nix
4343-module = aspect.resolve { class = "nixos"; aspect-chain = []; };
4444-```
4545-4646-Resolution collects the specified class from the aspect and all
4747-transitive includes into a single merged Nix module.
4848-4949-## den.aspects (option)
5050-5151-```nix
5252-options.den.aspects = lib.mkOption {
5353- type = aspectsType;
5454- default = { };
5555-};
5656-```
5757-5858-Contributions from any module are merged:
5959-6060-```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;
6666-```
6767-6868-## den.provides (batteries)
6969-7070-```nix
7171-options.den.provides = lib.mkOption {
7272- type = lib.types.submodule {
7373- freeformType = lib.types.attrsOf providerType;
7474- };
7575-};
7676-```
7777-7878-Access via `den._.name` or `den.provides.name`.
7979-See [Batteries Reference](/reference/batteries/) for all built-in aspects.
8080-8181-## den.default
8282-8383-The global dispatcher aspect:
8484-8585-```nix
8686-den.default = den.lib.parametric.atLeast { };
8787-```
8888-8989-All hosts, users, and homes include `den.default`. Set global
9090-configs and shared [parametric](/explanation/parametric/) includes here.
9191-9292-Aliased from `den.ctx.default`.
9393-9494-## Including Aspects
9595-9696-```nix
9797-den.aspects.igloo.includes = [
9898- # another aspect
9999- den.aspects.gaming
100100-101101- # nested provides
102102- den.aspects.tools._.editors
103103-104104- # static config
105105- { homeManager.programs.direnv.enable = true; }
106106-107107- # context function
108108- ({ host, ... }: { nixos.time.timeZone = "UTC"; })
109109-110110- # battery
111111- den._.define-user
112112-113113- # battery with args
114114- (den._.user-shell "fish")
115115- (den._.unfree [ "discord" ])
116116-];
117117-```
118118-119119-## Aspect as Module Argument
120120-121121-`den` is available as a module argument in all Den modules:
122122-123123-```nix
124124-{ den, ... }: {
125125- den.aspects.foo = den.lib.parametric { ... };
126126-}
127127-```
+74
docs/src/content/docs/reference/aspects.mdx
···11+---
22+title: den.aspects Reference
33+description: Aspect structure, resolution, and configuration.
44+---
55+66+import { Aside } from '@astrojs/starlight/components';
77+88+<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>
99+1010+## Aspect Attributes
1111+1212+Every aspect is an attribute set with these recognized keys:
1313+1414+| 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 |
2525+2626+## Automatic Aspect Creation
2727+2828+Den creates an aspect for each host, user, and home you declare:
2929+3030+```nix
3131+den.hosts.x86_64-linux.igloo.users.tux = { };
3232+3333+# will create:
3434+3535+den.aspects.igloo = parametric { };
3636+den.aspects.tux = parametric { };
3737+```
3838+3939+## Aspect Resolution
4040+4141+To extract a class module from an aspect use the flake-aspects API:
4242+4343+```nix
4444+module = aspect.resolve { class = "nixos"; aspect-chain = []; };
4545+```
4646+4747+Resolution collects the specified class from the aspect and all
4848+transitive includes into a single merged Nix module.
4949+5050+## den.aspects (namespace)
5151+5252+This namespace is where Den creates aspects for your host/user/home.
5353+5454+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`.
5757+5858+Contributions from any module are merged:
5959+6060+```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;
6666+```
6767+6868+## den.provides (namespace)
6969+7070+Den batteries-included re-usable aspects.
7171+7272+Access via `den._.name` or `den.provides.name`.
7373+7474+See [Batteries Reference](/reference/batteries/) for all built-in aspects.
···5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside type="tip">Source: [`modules/aspects/provides/`](https://github.com/vic/den/tree/main/modules/aspects/provides)</Aside>
88+<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>
991010## Overview
1111···1515## define-user
16161717Defines a user at OS and Home-Manager levels.
1818-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/define-user.nix))
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))
19192020```nix
2121den.default.includes = [ den._.define-user ];
···3131## primary-user
32323333Makes a user an administrator.
3434-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/primary-user.nix))
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))
35353636```nix
3737den.aspects.vic.includes = [ den._.primary-user ];
···4747## user-shell
48484949Sets default shell at OS and HM levels.
5050-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/user-shell.nix))
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))
51515252```nix
5353den.aspects.vic.includes = [ (den._.user-shell "fish") ];
···6363## unfree
64646565Enables 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))
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))
67676868```nix
6969den.aspects.laptop.includes = [ (den._.unfree [ "discord" ]) ];
···8282## tty-autologin
83838484Automatic 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))
85868687```nix
8788den.aspects.laptop.includes = [ (den._.tty-autologin "root") ];
···9495## import-tree
95969697Auto-imports non-dendritic Nix files by class directory.
9797-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/import-tree.nix))
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))
989999100```nix
100101den.aspects.laptop.includes = [ (den._.import-tree ./path) ];
···115116## inputs' (flake-parts)
116117117118Provides 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))
118120119121```nix
120122den.default.includes = [ den._.inputs' ];
···125127## self' (flake-parts)
126128127129Provides 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))
128131129132```nix
130133den.default.includes = [ den._.self' ];
···135138## forward
136139137140Creates custom Nix classes by forwarding configs between classes.
138138-([source](https://github.com/vic/den/blob/main/modules/aspects/provides/forward.nix) · [usage guide](/guides/custom-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.nix) · [usage guide](/guides/custom-classes/))
139142140143```nix
141144den._.forward {
···154157HM integration is handled by
155158`den.ctx.hm-host` and `den.ctx.hm-user` context types, which are
156159activated automatically when hosts have HM users.
160160+([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/))
157161158162All `homeManager` class settings are forwarded to os-level `home-manager.users.<userName>`.
159163160164## user
161165162166The `user` class is automatically handled by Den and forwards to os-level `users.users.<userName>`
167167+([source](https://github.com/vic/den/blob/main/modules/aspects/provides/os-user.nix))
163168164169You can write:
165170
-180
docs/src/content/docs/reference/ctx.md
···11----
22-title: den.ctx Reference
33-description: Context type definitions and their built-in implementations.
44----
55-66-import { Aside } from '@astrojs/starlight/components';
77-88-<Aside type="tip">Source: [`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)</Aside>
99-1010-## Context Type Schema
1111-1212-Each `den.ctx.<name>` is a submodule with these options:
1313-1414-| Option | Type | Description |
1515-|--------|------|-------------|
1616-| `desc` | `str` | Human-readable description |
1717-| `conf` | `ctx → aspect` | Locates the aspect for this context |
1818-| `into` | `attrset of (ctx → list)` | Transformations to other contexts |
1919-| `includes` | `list of aspect` | Parametric aspects activated for this context |
2020-2121-Context types are also functors — callable as functions:
2222-2323-```nix
2424-aspect = den.ctx.host { host = den.hosts.x86_64-linux.igloo; };
2525-```
2626-2727-## ctxApply (internal)
2828-2929-When a context type is applied, `ctxApply` executes:
3030-3131-```nix
3232-ctxApply = self: ctx: {
3333- includes = lib.flatten [
3434- (parametric.fixedTo ctx (cleanCtx self)) # owned configs
3535- (self.conf ctx) # located aspect
3636- (mapAttrsToList (name: into: # transformations
3737- map den.ctx.${name} (into ctx)
3838- ) self.into)
3939- ];
4040-};
4141-```
4242-4343-## Transformation Types
4444-4545-All `into` transformations have the type `source → [ target ]`:
4646-4747-- **Fan-out**: `{ host }: map (u: { inherit host user; }) users` — one host produces N contexts
4848-- **Conditional**: `{ host }: lib.optional (test host) { inherit host; }` — zero or one
4949-- **Pass-through**: `lib.singleton` — forward the same data as-is
5050-5151-An empty list means the target context is not created. This enables
5252-conditional activation like HM detection without any explicit `if` logic
5353-in the pipeline.
5454-5555-## Contexts as Cutting-Points
5656-5757-Context types have their own owned configs and `includes`, making them
5858-aspect-like cutting-points in the pipeline:
5959-6060-```nix
6161-den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true;
6262-den.ctx.hm-host.includes = [
6363- ({ host, ... }: { nixos.home-manager.backupFileExtension = "bak"; })
6464-];
6565-```
6666-6767-These only activate for validated HM hosts — more precise than
6868-`den.default.includes` which runs at every pipeline stage.
6969-7070-## Extending Context Flow
7171-7272-Add transformations to existing context types from any module:
7373-7474-```nix
7575-den.ctx.hm-host.into.foo = { host }: [ { foo = host.name; } ];
7676-den.ctx.foo.conf = { foo }: { funny.names = [ foo ]; };
7777-```
7878-7979-The module system merges these definitions. You can also override a
8080-host's `mainModule` to use a completely custom context flow.
8181-8282-## Built-in Context Types
8383-8484-### den.ctx.host
8585-8686-| Field | Value |
8787-|-------|-------|
8888-| `desc` | OS |
8989-| `conf` | `{ host }:` fixedTo host aspect |
9090-| `into.default` | `lib.singleton` (pass-through) |
9191-| `into.user` | Enumerate `host.users` |
9292-| `into.hm-host` | Detect HM support (from `hm-detect.nix`) |
9393-9494-### den.ctx.user
9595-9696-| Field | Value |
9797-|-------|-------|
9898-| `desc` | OS user |
9999-| `conf` | `{ host, user }:` includes user + host aspects |
100100-| `into.default` | `lib.singleton` (pass-through) |
101101-102102-### den.ctx.default
103103-104104-| Field | Value |
105105-|-------|-------|
106106-| `conf` | `_: { }` (empty — just a dispatcher) |
107107-108108-Aliased as `den.default` via `lib.mkAliasOptionModule`.
109109-Writing `den.default.foo` is identical to `den.ctx.default.foo`.
110110-111111-Every context type transforms into `default`, so `den.default.includes`
112112-functions run at every pipeline stage. Use `take.exactly` to restrict
113113-matching if you see duplicate values.
114114-115115-### den.ctx.home
116116-117117-| Field | Value |
118118-|-------|-------|
119119-| `desc` | Standalone Home-Manager config |
120120-| `conf` | `{ home }:` fixedTo home aspect |
121121-| `into.default` | `lib.singleton` |
122122-123123-### den.ctx.hm-host
124124-125125-| Field | Value |
126126-|-------|-------|
127127-| `desc` | Host with HM-supported OS and HM users |
128128-| `conf` | `{ host }:` imports HM NixOS/Darwin module |
129129-| `into.hm-user` | Enumerate HM-class users |
130130-131131-**Detection criteria** (all must be true):
132132-1. Host class is `nixos` or `darwin`
133133-2. At least one user has `class = "homeManager"`
134134-3. `inputs.home-manager` exists, or host has a custom `hm-module` attribute
135135-136136-If detection fails, the HM pipeline is skipped entirely.
137137-138138-### den.ctx.hm-user
139139-140140-| Field | Value |
141141-|-------|-------|
142142-| `desc` | Internal — forwards HM class to host |
143143-| `conf` | `{ host, user }:` forward homeManager into host |
144144-145145-### den.ctx.hm-internal-user (internal)
146146-147147-An internal context type used by `hm-user`. It combines:
148148-- The user context (`den.ctx.user { host, user }`)
149149-- Owned configs from the host aspect
150150-- Static includes from the host aspect
151151-- Parametric includes from the host aspect matching `{ host, user }`
152152-153153-This ensures that when the `homeManager` class is forwarded into
154154-`home-manager.users.<name>`, it receives contributions from both
155155-the user's aspect and the host's aspect.
156156-157157-## Defining Custom Context Types
158158-159159-Context types are independent of NixOS. Use them for any domain:
160160-161161-```nix
162162-den.ctx.myctx = {
163163- desc = "{x, y} context";
164164- conf = { x, y }: { funny.names = [ x y ]; };
165165- includes = [
166166- ({ x, ... }: { funny.names = [ "include-${x}" ]; })
167167- ];
168168- into = {
169169- other = { x, y }: [{ z = x + y; }];
170170- };
171171-};
172172-```
173173-174174-Owned attributes (anything not `desc`, `conf`, `into`, `includes`)
175175-are included when the context is applied.
176176-177177-Custom context flows can be designed and tested in isolation — Den's
178178-CI tests use a `funny.names` class that has nothing to do with NixOS
179179-to verify the context mechanics independently.See the [CI test suite](https://github.com/vic/den/tree/main/templates/ci/modules)
180180-for examples.
+169
docs/src/content/docs/reference/ctx.mdx
···11+---
22+title: den.ctx Reference
33+description: Context type definitions and their built-in implementations.
44+---
55+66+import { Aside } from '@astrojs/starlight/components';
77+88+<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 |
2323+2424+Context types are also functors — callable as functions:
2525+2626+```nix
2727+aspect = den.ctx.host { host = den.hosts.x86_64-linux.igloo; };
2828+```
2929+3030+## ctxApply (internal)
3131+3232+When a context type is applied, `ctxApply` walks the full `into` graph
3333+and deduplicates includes using a seen-set:
3434+3535+```nix
3636+ctxApply = ctxName: _self: ctx:
3737+ let
3838+ pairs = collectPairs null den.ctx.${ctxName} ctx;
3939+ in
4040+ { includes = dedupIncludes pairs; };
4141+```
4242+4343+**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.
4646+4747+**dedupIncludes** processes triples with a seen-set keyed by context name:
4848+4949+- **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
5151+5252+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)
5555+5656+## Transformation Types
5757+5858+All `into` transformations have the type `source → [ target ]`:
5959+6060+- **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
6363+6464+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.
6767+6868+## Contexts as Cutting-Points
6969+7070+Context types have their own owned configs and `includes`, making them
7171+aspect-like cutting-points in the pipeline:
7272+7373+```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+];
7878+```
7979+8080+8181+## Extending Context Flow
8282+8383+Add transformations to existing context types from any module:
8484+8585+```nix
8686+den.ctx.hm-host.into.foo = { host }: [ { foo = host.name; } ];
8787+den.ctx.foo._.foo = { foo }: { funny.names = [ foo ]; };
8888+```
8989+9090+The module system merges these definitions. You can also override a
9191+host's `mainModule` to use a completely custom context flow.
9292+9393+## Built-in Context Types
9494+9595+### den.ctx.host
9696+9797+| 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-detect.nix`) |
105105+106106+### den.ctx.user
107107+108108+| Field | Value |
109109+|-------|-------|
110110+| `description` | OS user |
111111+| `provides.user` | `{ host, user }:` fixedTo user aspect |
112112+| `into.default` | `lib.singleton` (pass-through) |
113113+114114+### den.ctx.default
115115+116116+| Field | Value |
117117+|-------|-------|
118118+| `provides.default` | `_: { }` (empty — just a dispatcher) |
119119+120120+Aliased as `den.default` via `lib.mkAliasOptionModule`.
121121+Writing `den.default.foo` is identical to `den.ctx.default.foo`.
122122+123123+Host/user/home context types transforms into `default`, so `den.default.includes`
124124+functions run at each of their pipeline stages. However, owned and static configs
125125+are **automatically deduplicated** — only the first visit gets them.
126126+Parametric functions still run at every stage; use `take.exactly` to
127127+restrict matching if needed.
128128+129129+### den.ctx.home
130130+131131+| Field | Value |
132132+|-------|-------|
133133+| `description` | Standalone Home-Manager config |
134134+| `provides.home` | `{ home }:` fixedTo home aspect |
135135+| `into.default` | `lib.singleton` |
136136+137137+### den.ctx.hm-host
138138+139139+| Field | Value |
140140+|-------|-------|
141141+| `description` | Host with HM-supported OS and HM users |
142142+| `provides.hm-host` | `{ host }:` imports HM NixOS/Darwin module |
143143+| `into.hm-user` | Enumerate HM-class users |
144144+145145+**Detection criteria** (all must be true):
146146+1. Host class is `nixos` or `darwin`
147147+2. At least one user has `class = "homeManager"`
148148+3. `inputs.home-manager` exists, or host has a custom `hm-module` attribute
149149+150150+If detection fails, the HM pipeline is skipped entirely.
151151+152152+### den.ctx.hm-user
153153+154154+| Field | Value |
155155+|-------|-------|
156156+| `description` | Internal — forwards HM class to host |
157157+| `provides.hm-user` | `{ host, user }:` forward homeManager into host |
158158+159159+### den.ctx.hm-internal-user (internal)
160160+161161+An internal context type used by `hm-user`. It combines:
162162+- The user context (`den.ctx.user { host, user }`)
163163+- Owned configs from the host aspect
164164+- Static includes from the host aspect
165165+- Parametric includes from the host aspect matching `{ host, user }`
166166+167167+This ensures that when the `homeManager` class is forwarded into
168168+`home-manager.users.<name>`, it receives contributions from both
169169+the user's aspect and the host's aspect.
···5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside type="tip">Source: [`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)</Aside>
88+<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>
991010## den.lib.parametric
11111212-Creates a parametric aspect. Alias for `parametric.withOwn parametric.atLeast`.
1212+Creates a parametric aspect. Alias for `(parametric.withOwn parametric.atLeast)`.
13131414```nix
1515den.lib.parametric { nixos.x = 1; includes = [ f ]; }
···21212222Dispatches **only** to functions whose required args are a subset of context:
23232424+**does include owned config, atLeast only forwards context**.
2525+2426```nix
2527F = parametric.atLeast { includes = [ a b ]; };
2628```
···2830### parametric.exactly
29313032Dispatches **only** to functions whose args match context exactly:
3333+3434+**does include owned config, atLeast only forwards context**.
31353236```nix
3337F = parametric.exactly { includes = [ a b ]; };
···37413842Replaces context with a fixed attribute set:
39434444+Ignores any given context at call site, and replaces with a fixed one.
4545+Behaves like `parametric`, includes owned + static includes + function includes.
4646+4047```nix
4148F = parametric.fixedTo { x = 1; } aspect;
4249```
···4451### parametric.expands
45524653Merges extra attributes into received context:
5454+Behaves like `parametric`, includes owned + static includes + function includes.
47554856```nix
4957F = parametric.expands { extra = 1; } aspect;
···5260### parametric.withOwn
53615462Combinator: adds owned + statics on top of a dispatch functor:
6363+This is how `parametric` function itself uses `parametric.atLeast`.
55645665```nix
6666+# an exactly variant of parametric.
5767F = parametric.withOwn parametric.exactly aspect;
5868```
5969···69797080Wraps function to only be called when context has at least the required args.
71818282+Produces an empty attrset if the context has not atLeast the expected args.
8383+7284### take.exactly
73857486```nix
···7688```
77897890Only called when context has exactly these args, no more.
9191+9292+Produces an empty attrset otherwise.
79938094### take.unused
8195···8910390104Function signature introspection:
91105106106+Does not care about values, only about attribute names.
107107+92108```nix
93109den.lib.canTake { x = 1; } someFunction
94110# => true if someFunction can take at least { x }
···99115100116## den.lib.aspects
101117102102-Re-export of `flake-aspects` library. Provides:
118118+Re-export of [`flake-aspects`](https://github.com/vic/flake-aspects) library. Provides:
103119- `aspects.types.aspectsType` — module type for aspect trees
104120- `aspects.types.providerType` — type for aspect providers
105121- `aspects.forward` — class forwarding implementation
···114130115131## den.lib.owned
116132117117-Extracts only owned configs from an aspect (removes `includes`, `__functor`):
133133+Extracts only owned configs from an aspect (**removes** `includes`, `__functor` from someAspect):
118134119135```nix
120136den.lib.owned someAspect # => { nixos = ...; darwin = ...; }
···124140125141Creates a functor that only resolves static includes from an aspect:
126142143143+The new aspect ignores owned configs, and functional includes that are not
144144+of type (ground flake-aspects): `{ class, aspect-chain }: {...}`
145145+127146```nix
128128-den.lib.statics someAspect { class = "nixos"; aspect-chain = []; }
147147+den.lib.statics someAspect # => <aspect with only static includes>
129148```
130149131150## den.lib.isStatic
···5566import { Aside } from '@astrojs/starlight/components';
7788-<Aside type="tip">Source: [`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)</Aside>
88+99+<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>
1010+1111+`den.base` provides extensible schemas for `host`/`user`/`home`.
9121013## den.hosts
1114···9295```
93969497`den.base.conf` is automatically imported by host, user, and home.
9595-9696-## den.ful
9797-9898-Namespace storage for aspect libraries:
9999-100100-```nix
101101-den.ful.<namespace>.<name> = aspect;
102102-```
103103-104104-Populated via [`inputs.den.namespace`](/guides/namespaces/).
105105-106106-## flake.denful
107107-108108-Flake output for sharing namespaces:
109109-110110-```nix
111111-flake.denful.<namespace> = den.ful.<namespace>;
112112-```
+109
docs/src/content/docs/tutorials/bogus.md
···11+---
22+title: "Template: Bug Reproduction"
33+description: Create minimal reproductions for Den bug reports using nix-unit.
44+---
55+66+The bogus template helps you create minimal bug reproductions. Use it when reporting issues or contributing fixes.
77+88+## Initialize
99+1010+```console
1111+mkdir bogus && cd bogus
1212+nix flake init -t github:vic/den#bogus
1313+nix flake update den
1414+```
1515+1616+## Project Structure
1717+1818+```
1919+flake.nix
2020+modules/
2121+ bug.nix # your bug reproduction
2222+ test-base.nix # test infrastructure (DO NOT EDIT)
2323+```
2424+2525+## Writing a Bug Reproduction
2626+2727+Edit `modules/bug.nix` with a minimal test case:
2828+2929+```nix
3030+{ denTest, ... }:
3131+{
3232+ flake.tests.bogus = {
3333+ test-something = denTest (
3434+ { den, lib, igloo, tuxHm, ... }:
3535+ {
3636+ den.hosts.x86_64-linux.igloo.users.tux = { };
3737+3838+ # set up the scenario
3939+ den.aspects.igloo.nixos.something = true;
4040+4141+ # what you get
4242+ expr = igloo.something;
4343+ # what you expect
4444+ expected = true;
4545+ }
4646+ );
4747+ };
4848+}
4949+```
5050+5151+### How denTest Works
5252+5353+`denTest` is a helper that:
5454+1. Creates a fresh Den evaluation with your module
5555+2. Provides helpers like `igloo` (host config), `tuxHm` (user's HM config)
5656+3. Compares `expr` against `expected` using [nix-unit](https://github.com/nix-community/nix-unit)
5757+5858+Available test helpers (from `test-base.nix`):
5959+6060+| Helper | Description |
6161+|--------|-------------|
6262+| `igloo` | `nixosConfigurations.igloo.config` |
6363+| `iceberg` | `nixosConfigurations.iceberg.config` |
6464+| `tuxHm` | `igloo.home-manager.users.tux` |
6565+| `pinguHm` | `igloo.home-manager.users.pingu` |
6666+| `funnyNames` | Resolves an aspect for class `"funny"` and collects `.names` |
6767+| `show` | `builtins.trace` helper for debugging |
6868+6969+## Run Tests
7070+7171+```console
7272+nix flake check
7373+```
7474+7575+## Testing Against Different Den Versions
7676+7777+Edit `.github/workflows/test.yml` to test against multiple Den versions:
7878+7979+```yaml
8080+strategy:
8181+ matrix:
8282+ rev: ["main", "v1.0.0", "abc1234"]
8383+```
8484+8585+This helps identify regressions — include `"main"` and any release tag or commit.
8686+8787+## Contributing a Fix
8888+8989+If you're submitting a fix to Den, test against your local checkout:
9090+9191+```console
9292+cd <den-working-copy>
9393+nix flake check --override-input den . ./templates/bogus
9494+```
9595+9696+## What It Provides
9797+9898+| Feature | Provided |
9999+|---------|:--------:|
100100+| nix-unit test infrastructure | ✓ |
101101+| Pre-configured denTest helper | ✓ |
102102+| Version matrix testing | ✓ |
103103+| Common test helpers | ✓ |
104104+105105+## Next Steps
106106+107107+- Share your reproduction repo on [GitHub Discussions](https://github.com/vic/den/discussions)
108108+- Read [Debug Configurations](/guides/debug/) for debugging techniques
109109+- See the [CI Tests template](/tutorials/ci/) for Den's own comprehensive test suite
+164
docs/src/content/docs/tutorials/ci.md
···11+---
22+title: "Template: CI Tests"
33+description: Den's own test suite — the definitive reference for every feature.
44+---
55+66+The CI template is Den's comprehensive test suite. It tests every feature using [nix-unit](https://github.com/nix-community/nix-unit). This is the **best learning resource** for understanding exactly how Den behaves.
77+88+## Structure
99+1010+```
1111+flake.nix
1212+modules/
1313+ empty.nix # example test skeleton
1414+ test-support/
1515+ eval-den.nix # denTest + evalDen helpers
1616+ nix-unit.nix # nix-unit integration
1717+ features/
1818+ angle-brackets.nix # <den/...> syntax
1919+ conditional-config.nix # conditional imports/configs
2020+ default-includes.nix # den.default behavior
2121+ forward.nix # den._.forward
2222+ homes.nix # standalone HM
2323+ host-options.nix # host/user schema options
2424+ namespaces.nix # namespace define/merge/export
2525+ os-user-class.nix # user class forwarding
2626+ parametric.nix # parametric functors
2727+ schema-base-modules.nix # den.base modules
2828+ special-args-custom-instantiate.nix # custom instantiation
2929+ top-level-parametric.nix # top-level context aspects
3030+ user-host-bidirectional-config.nix # bidirectional providers
3131+ batteries/
3232+ define-user.nix # define-user battery
3333+ flake-parts.nix # inputs' and self'
3434+ import-tree.nix # import-tree battery
3535+ primary-user.nix # primary-user battery
3636+ tty-autologin.nix # tty-autologin battery
3737+ unfree.nix # unfree packages
3838+ user-shell.nix # user-shell battery
3939+ context/
4040+ apply.nix # ctx application
4141+ apply-non-exact.nix # non-exact matching
4242+ cross-provider.nix # cross-provider mechanism
4343+ custom-ctx.nix # custom context types
4444+ den-default.nix # den.default as context
4545+ host-propagation.nix # full host pipeline
4646+ named-provider.nix # self-named providers
4747+ deadbugs/
4848+ _external-namespace-deep-aspect.nix
4949+ static-include-dup-package.nix
5050+ home-manager/
5151+ home-managed-home.nix
5252+ use-global-pkgs.nix
5353+ non-dendritic/ # non-den files for import-tree tests
5454+ provider/ # external namespace provider flake
5555+```
5656+5757+## Test Categories
5858+5959+### Core Features
6060+6161+| Test File | What It Tests |
6262+|-----------|---------------|
6363+| [conditional-config.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/conditional-config.nix) | Conditional imports using host/user attributes |
6464+| [default-includes.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/default-includes.nix) | `den.default` applying to all hosts/users |
6565+| [host-options.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/host-options.nix) | Custom host attributes, hostName, aspect names |
6666+| [top-level-parametric.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/top-level-parametric.nix) | Context-aware top-level aspects |
6767+| [parametric.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/parametric.nix) | All parametric functors (atLeast, fixedTo, expands) |
6868+6969+### Bidirectional & Providers
7070+7171+| Test File | What It Tests |
7272+|-----------|---------------|
7373+| [user-host-bidirectional-config.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/user-host-bidirectional-config.nix) | Host→user and user→host config flow |
7474+| [context/cross-provider.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/cross-provider.nix) | Source providing config to target context |
7575+| [context/named-provider.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/named-provider.nix) | Self-named provider mechanism |
7676+7777+### Context System
7878+7979+| Test File | What It Tests |
8080+|-----------|---------------|
8181+| [context/apply.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/apply.nix) | Context application mechanics |
8282+| [context/apply-non-exact.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/apply-non-exact.nix) | Non-exact context matching |
8383+| [context/custom-ctx.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/custom-ctx.nix) | User-defined context types with `into` |
8484+| [context/den-default.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/den-default.nix) | `den.default` as a context type |
8585+| [context/host-propagation.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/host-propagation.nix) | Full host pipeline with all contributions |
8686+8787+### Batteries
8888+8989+| Test File | What It Tests |
9090+|-----------|---------------|
9191+| [batteries/define-user.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/define-user.nix) | User definition across contexts |
9292+| [batteries/primary-user.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/primary-user.nix) | Primary user groups |
9393+| [batteries/user-shell.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/user-shell.nix) | Shell configuration |
9494+| [batteries/unfree.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/unfree.nix) | Unfree package predicates |
9595+| [batteries/tty-autologin.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/tty-autologin.nix) | TTY autologin service |
9696+| [batteries/import-tree.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/import-tree.nix) | Auto-importing class dirs |
9797+| [batteries/flake-parts.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/flake-parts.nix) | `inputs'` and `self'` providers |
9898+9999+### Advanced
100100+101101+| Test File | What It Tests |
102102+|-----------|---------------|
103103+| [angle-brackets.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/angle-brackets.nix) | All bracket resolution paths |
104104+| [namespaces.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/namespaces.nix) | Local, remote, merged namespaces |
105105+| [forward.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/forward.nix) | Custom class forwarding |
106106+| [homes.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/homes.nix) | Standalone Home-Manager configs |
107107+| [schema-base-modules.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/schema-base-modules.nix) | `den.base.{host,user,home,conf}` |
108108+109109+### Bug Regressions
110110+111111+| Test File | What It Tests |
112112+|-----------|---------------|
113113+| [deadbugs/static-include-dup-package.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/deadbugs/static-include-dup-package.nix) | Duplicate deduplication for packages/lists |
114114+| [deadbugs/_external-namespace-deep-aspect.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/deadbugs/_external-namespace-deep-aspect.nix) | Deep aspect access from external flakes |
115115+116116+### External Provider
117117+118118+The `provider/` subdirectory is a **separate flake** that defines a namespace `provider` with aspects. It's used by the deadbugs test to verify cross-flake namespace consumption:
119119+120120+```nix
121121+# provider/modules/den.nix
122122+{ inputs, ... }:
123123+{
124124+ imports = [ inputs.den.flakeModule (inputs.den.namespace "provider" true) ];
125125+ provider.tools._.dev._.editors = {
126126+ nixos.programs.vim.enable = true;
127127+ };
128128+}
129129+```
130130+131131+## Running CI Tests
132132+133133+From the Den root against your local checkout:
134134+135135+```console
136136+nix flake check --override-input den . ./templates/ci
137137+```
138138+139139+You can also run a single or a subset of tests using:
140140+141141+```console
142142+# You can use any attr-path bellow flake.tests after system-agnositc to run those specific tests:
143143+nix-unit --override-input den . --flake ./templates/ci#.tests.systems.x86_64-linux.system-agnostic
144144+```
145145+146146+## Writing New Tests
147147+148148+Copy `modules/empty.nix` as a starting point:
149149+150150+```nix
151151+{ denTest, ... }:
152152+{
153153+ flake.tests.my-feature = {
154154+ test-name = denTest (
155155+ { den, igloo, ... }:
156156+ {
157157+ den.hosts.x86_64-linux.igloo.users.tux = { };
158158+ expr = /* what you get */;
159159+ expected = /* what you expect */;
160160+ }
161161+ );
162162+ };
163163+}
164164+```
-115
docs/src/content/docs/tutorials/context-aware.md
···11----
22-title: Context-Aware Configurations
33-description: Make aspects produce conditional config based on host and user context.
44----
55-66-## From Static to Dynamic
77-88-So far, aspects have been static attribute sets. But Den's power comes from
99-making them **functions of context**. When an aspect is a function, Den
1010-passes it the current context — host, user, platform — and the function
1111-decides what to produce.
1212-1313-## Step 1: A Simple Context Function
1414-1515-Instead of a static aspect, write a function:
1616-1717-```nix
1818-{ den, ... }: {
1919- den.aspects.igloo.includes = [
2020- ({ host, ... }: {
2121- nixos.networking.hostName = host.name;
2222- })
2323- ];
2424-}
2525-```
2626-2727-Den calls this function with `{ host }` containing the host's attributes.
2828-The result sets the hostname dynamically from the host's name.
2929-3030-## Step 2: React to User Context
3131-3232-Functions receiving `{ host, user, ... }` run once per user on each host:
3333-3434-```nix
3535-{ den, ... }: {
3636- den.default.includes = [
3737- ({ host, user, ... }: {
3838- ${host.class}.users.users.${user.userName}.description =
3939- "${user.userName} on ${host.name}";
4040- })
4141- ];
4242-}
4343-```
4444-4545-Notice `${host.class}` — this dynamically targets `nixos` or `darwin`
4646-depending on the host's platform. The same function works on both.
4747-4848-## Step 3: Conditional Configuration
4949-5050-Use standard Nix conditionals inside context functions:
5151-5252-```nix
5353-{ den, lib, ... }:
5454-let
5555- git-for-linux = { user, host, ... }:
5656- if !lib.hasSuffix "darwin" host.system
5757- then { homeManager.programs.git.enable = true; }
5858- else { };
5959-in {
6060- den.aspects.tux.includes = [ git-for-linux ];
6161-}
6262-```
6363-6464-Git is enabled only for users on Linux hosts. On Darwin, the function
6565-returns an empty set — no effect.
6666-6767-## Step 4: Use den.lib.parametric
6868-6969-For aspects that need to forward context to their own includes,
7070-wrap them with `den.lib.parametric`:
7171-7272-```nix
7373-{ den, ... }:
7474-let
7575- workspace = den.lib.parametric {
7676- nixos.networking.hostName = "from-parametric";
7777- includes = [
7878- ({ host, ... }: { nixos.time.timeZone = "UTC"; })
7979- ];
8080- };
8181-in {
8282- den.aspects.igloo.includes = [ workspace ];
8383-}
8484-```
8585-8686-The `parametric` functor ensures that:
8787-1. **Owned** config (`nixos.networking.hostName`) is always included
8888-2. **Functions** in `includes` receive the forwarded context
8989-3. Functions whose parameters don't match are **silently skipped**
9090-9191-## Step 5: Understand Matching
9292-9393-Den matches context by **argument names**, not values:
9494-9595-| Function signature | `{ host }` | `{ host, user }` |
9696-|--------------------|:---:|:---:|
9797-| `{ host, ... }: ...` | ✓ | ✓ |
9898-| `{ host, user }: ...` | ✗ | ✓ |
9999-| `{ never, ... }: ...` | ✗ | ✗ |
100100-101101-- `atLeast` — function is called if context has **at least** the required args
102102-- `exactly` — function is called only if context has **exactly** those args
103103-104104-## What You've Learned
105105-106106-- Aspects can be functions that receive `{ host, user, home, ... }`
107107-- Context functions produce conditional, platform-aware configurations
108108-- [`den.lib.parametric`](/explanation/parametric/) forwards context to nested includes
109109-- Unmatched functions are [silently skipped](/explanation/parametric/#matching-rules-summary) — no errors
110110-- `${host.class}` makes configs target the right Nix class dynamically
111111-112112-## Next
113113-114114-- [Bidirectional Dependencies](/guides/bidirectional/) — hosts and users configure each other
115115-- [Context System](/explanation/context-system/) — deep dive into `den.ctx`
+177
docs/src/content/docs/tutorials/default.md
···11+---
22+title: "Template: Default"
33+description: Recommended starting point with flake-parts, Home-Manager, and VM testing.
44+---
55+66+The default template is the recommended way to start a new Den project. It includes flake-parts, Home-Manager, dendritic flake-file, and a VM for testing.
77+88+## Initialize
99+1010+```console
1111+mkdir my-nix && cd my-nix
1212+nix flake init -t github:vic/den#default
1313+nix flake update den
1414+```
1515+1616+## Project Structure
1717+1818+```
1919+flake.nix # auto-generated by flake-file
2020+modules/
2121+ dendritic.nix # flake-file + den dendritic setup
2222+ hosts.nix # host and user declarations
2323+ defaults.nix # global settings (stateVersion, etc.)
2424+ igloo.nix # host aspect
2525+ tux.nix # user aspect
2626+ vm.nix # VM runner
2727+```
2828+2929+## File by File
3030+3131+### hosts.nix — Declare Your Infrastructure
3232+3333+```nix
3434+{
3535+ den.hosts.x86_64-linux.igloo.users.tux = { };
3636+}
3737+```
3838+3939+This single line declares:
4040+- A host `igloo` on `x86_64-linux` (class: `nixos`)
4141+- A user `tux` on that host (class: `homeManager`)
4242+4343+The host and user each get an aspect (`den.aspects.igloo` and `den.aspects.tux`) that you configure in separate files.
4444+4545+### igloo.nix — Host Aspect
4646+4747+```nix
4848+{
4949+ den.aspects.igloo = {
5050+ nixos = { pkgs, ... }: {
5151+ environment.systemPackages = [ pkgs.hello ];
5252+ };
5353+ homeManager = { pkgs, ... }: {
5454+ home.packages = [ pkgs.vim ];
5555+ };
5656+ };
5757+}
5858+```
5959+6060+The host aspect provides:
6161+- **NixOS config** — system packages available to all users
6262+- **Home-Manager config** — default home environment for every user on this host
6363+6464+### tux.nix — User Aspect
6565+6666+```nix
6767+{ den, ... }:
6868+{
6969+ den.aspects.tux = {
7070+ includes = [
7171+ den.provides.primary-user
7272+ (den.provides.user-shell "fish")
7373+ ];
7474+ homeManager = { pkgs, ... }: {
7575+ home.packages = [ pkgs.htop ];
7676+ };
7777+ };
7878+}
7979+```
8080+8181+The user aspect:
8282+- Includes [`primary-user`](/reference/batteries/#primary-user) — adds wheel + networkmanager groups
8383+- Includes [`user-shell`](/reference/batteries/#user-shell) — sets fish as default shell at OS and HM level
8484+- Provides personal Home-Manager packages
8585+8686+### defaults.nix — Global Settings
8787+8888+```nix
8989+{
9090+ den.default.nixos.system.stateVersion = "25.11";
9191+ den.default.homeManager.home.stateVersion = "25.11";
9292+}
9393+```
9494+9595+`den.default` applies settings to **all** hosts, users, and homes. This is the right place for `stateVersion` and other global policies.
9696+9797+### vm.nix — Test in a VM
9898+9999+```nix
100100+{ inputs, den, ... }:
101101+{
102102+ den.aspects.igloo.includes = [ (den.provides.tty-autologin "tux") ];
103103+104104+ perSystem = { pkgs, ... }: {
105105+ packages.vm = pkgs.writeShellApplication {
106106+ name = "vm";
107107+ text = let
108108+ host = inputs.self.nixosConfigurations.igloo.config;
109109+ in ''
110110+ ${host.system.build.vm}/bin/run-${host.networking.hostName}-vm "$@"
111111+ '';
112112+ };
113113+ };
114114+}
115115+```
116116+117117+Run the VM with:
118118+119119+```console
120120+nix run .#vm
121121+```
122122+123123+### dendritic.nix — Flake Wiring
124124+125125+```nix
126126+{ inputs, ... }:
127127+{
128128+ imports = [
129129+ (inputs.flake-file.flakeModules.dendritic or { })
130130+ (inputs.den.flakeModules.dendritic or { })
131131+ ];
132132+ flake-file.inputs = {
133133+ den.url = "github:vic/den";
134134+ flake-file.url = "github:vic/flake-file";
135135+ home-manager = {
136136+ url = "github:nix-community/home-manager";
137137+ inputs.nixpkgs.follows = "nixpkgs";
138138+ };
139139+ };
140140+}
141141+```
142142+143143+This uses [flake-file](https://github.com/vic/flake-file) so inputs can be defined close to where they are used. Run `nix run .#write-flake` to regenerate `flake.nix` after changing inputs.
144144+145145+## Data Flow
146146+147147+```mermaid
148148+graph TD
149149+ H["hosts.nix: igloo + tux"] --> CH["ctx.host {host=igloo}"]
150150+ CH --> AI["den.aspects.igloo<br/>nixos + homeManager"]
151151+ CH --> CU["ctx.user {host=igloo, user=tux}"]
152152+ CU --> AT["den.aspects.tux<br/>primary-user + fish + htop"]
153153+ CH --> HM["ctx.hm-host: import HM module"]
154154+ HM --> HMU["ctx.hm-user: forward homeManager<br/>into home-manager.users.tux"]
155155+ AI --> NixOS["nixosConfigurations.igloo"]
156156+ AT --> NixOS
157157+ HMU --> NixOS
158158+```
159159+160160+## What It Provides
161161+162162+| Feature | Provided |
163163+|---------|:--------:|
164164+| NixOS host configuration | ✓ |
165165+| Home-Manager integration | ✓ |
166166+| Dendritic flake-file | ✓ |
167167+| VM testing | ✓ |
168168+| flake-parts | ✓ |
169169+| Darwin support | Add input |
170170+| Namespaces | Add manually |
171171+172172+## Next Steps
173173+174174+- Edit `hosts.nix` to add more hosts or users
175175+- Create new aspect files under `modules/`
176176+- Add Darwin support by adding `nix-darwin` input
177177+- Explore the [Example template](/tutorials/example/) for namespaces and advanced features
+183
docs/src/content/docs/tutorials/example.md
···11+---
22+title: "Template: Example"
33+description: Feature showcase with namespaces, angle brackets, cross-platform aspects, and providers.
44+---
55+66+The example template demonstrates Den's advanced features: namespaces, angle brackets, cross-platform hosts, bidirectional providers, and custom aspect libraries.
77+88+## Initialize
99+1010+```console
1111+mkdir my-nix && cd my-nix
1212+nix flake init -t github:vic/den#example
1313+nix flake update den
1414+```
1515+1616+## Project Structure
1717+1818+```
1919+flake.nix
2020+modules/
2121+ den.nix # host/home declarations
2222+ dendritic.nix # flake-file + den wiring
2323+ inputs.nix # flake inputs
2424+ namespace.nix # creates the "eg" namespace
2525+ tests.nix # CI checks
2626+ vm.nix # VM runner
2727+ aspects/
2828+ defaults.nix # global config + angle brackets demo
2929+ alice.nix # user aspect
3030+ igloo.nix # host aspect
3131+ eg/ # namespace aspects
3232+ autologin.nix
3333+ ci-no-boot.nix
3434+ routes.nix
3535+ vm.nix
3636+ vm-bootable.nix
3737+ xfce-desktop.nix
3838+```
3939+4040+## Key Features Demonstrated
4141+4242+### Cross-Platform Hosts
4343+4444+```nix
4545+# modules/den.nix
4646+{
4747+ den.hosts.x86_64-linux.igloo.users.alice = { };
4848+ den.hosts.aarch64-darwin.apple.users.alice = { };
4949+ den.homes.x86_64-linux.alice = { };
5050+}
5151+```
5252+5353+One user (`alice`) across a NixOS host, a Darwin host, and a standalone Home-Manager config. The same aspects produce appropriate configs for each platform.
5454+5555+### Namespaces
5656+5757+```nix
5858+# modules/namespace.nix
5959+{ inputs, den, ... }:
6060+{
6161+ imports = [ (inputs.den.namespace "eg" true) ];
6262+ _module.args.__findFile = den.lib.__findFile;
6363+}
6464+```
6565+6666+Creates a local namespace `eg` accessible as a module argument. The `true` flag exposes it as a flake output (`flake.denful.eg`). Angle brackets are enabled via `__findFile`.
6767+6868+### Namespace Aspects
6969+7070+The `eg/` directory defines reusable aspects under the `eg` namespace:
7171+7272+```nix
7373+# modules/aspects/eg/vm.nix
7474+{ eg, ... }:
7575+{
7676+ eg.vm.provides = {
7777+ gui.includes = [ eg.vm eg.vm-bootable._.gui eg.xfce-desktop ];
7878+ tui.includes = [ eg.vm eg.vm-bootable._.tui ];
7979+ };
8080+}
8181+```
8282+8383+Aspects can have nested **provides** — `eg.vm._.gui` and `eg.vm._.tui` are sub-aspects accessed via the `_.` provider syntax.
8484+8585+### Angle Brackets
8686+8787+```nix
8888+# modules/aspects/defaults.nix
8989+{ den, __findFile ? __findFile, ... }:
9090+{
9191+ den.default.includes = [
9292+ <eg/routes> # resolves to eg.routes
9393+ <den/define-user> # resolves to den.provides.define-user
9494+ ];
9595+}
9696+```
9797+9898+The `<name>` syntax is shorthand for aspect lookup. `<den/define-user>` resolves to `den.provides.define-user`. See [Angle Brackets](/guides/angle-brackets/).
9999+100100+### Bidirectional Providers
101101+102102+```nix
103103+# modules/aspects/alice.nix — user provides config TO the host
104104+den.aspects.alice = {
105105+ provides.igloo = { host, ... }: {
106106+ nixos.programs.nh.enable = host.name == "igloo";
107107+ };
108108+};
109109+110110+# modules/aspects/igloo.nix — host provides config TO the user
111111+den.aspects.igloo = {
112112+ provides.alice = { user, ... }: {
113113+ homeManager.programs.helix.enable = user.name == "alice";
114114+ };
115115+};
116116+```
117117+118118+Hosts and users can contribute configuration **to each other** through `provides`. Alice enables `nh` on igloo, and igloo enables `helix` for alice. These are cross-providers activated during context transformation.
119119+120120+### Custom Routes
121121+122122+```nix
123123+# modules/aspects/eg/routes.nix
124124+{ den, ... }:
125125+{
126126+ eg.routes = let
127127+ inherit (den.lib) parametric;
128128+ mutual = from: to: den.aspects.${from.aspect}._.${to.aspect} or { };
129129+ routes = { host, user, ... }@ctx:
130130+ parametric.fixedTo ctx {
131131+ includes = [ (mutual user host) (mutual host user) ];
132132+ };
133133+ in routes;
134134+}
135135+```
136136+137137+This aspect **wires bidirectional providers** between hosts and users automatically. Including `<eg/routes>` in `den.default` activates all `provides` declarations.
138138+139139+### Tests
140140+141141+```nix
142142+# modules/tests.nix — verifies the template works
143143+{
144144+ checks."alice enabled igloo nh" = checkCond "..." igloo.programs.nh.enable;
145145+ checks."igloo enabled alice helix" = checkCond "..." alice-at-igloo.programs.helix.enable;
146146+}
147147+```
148148+149149+## Data Flow
150150+151151+```mermaid
152152+graph TD
153153+ D["den.nix: igloo/apple + alice"] --> CH["ctx.host"]
154154+ CH --> AI["aspects.igloo<br/>nixos + provides.alice"]
155155+ CH --> CU["ctx.user"]
156156+ CU --> AA["aspects.alice<br/>includes + provides.igloo"]
157157+ AA -->|"routes: mutual"| AI
158158+ AI -->|"routes: mutual"| AA
159159+ CH --> HM["ctx.hm-host"]
160160+ AI --> NixOS["nixosConfigurations.igloo"]
161161+ AA --> NixOS
162162+ AI --> Darwin["darwinConfigurations.apple"]
163163+ AA --> Darwin
164164+```
165165+166166+## What It Provides
167167+168168+| Feature | Provided |
169169+|---------|:--------:|
170170+| NixOS + Darwin hosts | ✓ |
171171+| Home-Manager | ✓ |
172172+| Standalone HM config | ✓ |
173173+| Namespaces (`eg`) | ✓ |
174174+| Angle brackets | ✓ |
175175+| Bidirectional providers | ✓ |
176176+| VM testing | ✓ |
177177+| CI checks | ✓ |
178178+179179+## Next Steps
180180+181181+- Study the `eg/` namespace aspects as examples for your own libraries
182182+- Read [Namespaces](/guides/namespaces/) and [Angle Brackets](/guides/angle-brackets/)
183183+- Explore the [CI Tests template](/tutorials/ci/) for comprehensive feature coverage
-114
docs/src/content/docs/tutorials/first-aspect.md
···11----
22-title: Your First Aspect
33-description: Create a cross-class, reusable Nix configuration aspect.
44----
55-66-## What is an Aspect?
77-88-An aspect is a composable bundle of Nix configurations that can span multiple
99-classes (NixOS, Darwin, Home-Manager). Each host, user, and home you declare
1010-automatically gets an aspect.
1111-1212-## Step 1: Declare Your Infrastructure
1313-1414-Start with a host and user:
1515-1616-```nix
1717-# modules/hosts.nix
1818-{
1919- den.hosts.x86_64-linux.igloo.users.tux = { };
2020-}
2121-```
2222-2323-Den creates `den.aspects.igloo` (host) and `den.aspects.tux` (user) automatically.
2424-2525-## Step 2: Configure the Host Aspect
2626-2727-Any module file can contribute to any aspect. Create a file for your host:
2828-2929-```nix
3030-# modules/igloo.nix
3131-{ den, ... }: {
3232- den.aspects.igloo = {
3333- nixos.networking.hostName = "igloo";
3434- nixos.time.timeZone = "UTC";
3535- homeManager.programs.direnv.enable = true;
3636- };
3737-}
3838-```
3939-4040-The `nixos` settings apply to NixOS. The `homeManager` settings apply
4141-to **every user** on this host.
4242-4343-## Step 3: Configure the User Aspect
4444-4545-```nix
4646-# modules/vic.nix
4747-{ den, ... }: {
4848- den.aspects.tux = {
4949- homeManager.programs.fish.enable = true;
5050- nixos.users.users.tux.description = "Tux the Penguin";
5151- };
5252-}
5353-```
5454-5555-User aspects contribute to **every host** that has this user.
5656-If `tux` exists on both `igloo` and another host, both get the fish config.
5757-5858-## Step 4: Use Includes for Composition
5959-6060-Aspects can include other aspects:
6161-6262-```nix
6363-# modules/gaming.nix
6464-{ den, ... }: {
6565- den.aspects.gaming = {
6666- nixos.programs.steam.enable = true;
6767- homeManager.programs.mangohud.enable = true;
6868- };
6969-7070- den.aspects.igloo.includes = [ den.aspects.gaming ];
7171-}
7272-```
7373-7474-## Step 5: Use Provides for Nesting
7575-7676-Organize related aspects in a tree:
7777-7878-```nix
7979-{ den, ... }: {
8080- den.aspects.tools.provides.editors = {
8181- homeManager.programs.vim.enable = true;
8282- };
8383-8484- den.aspects.tux.includes = [ den.aspects.tools._.editors ];
8585-}
8686-```
8787-8888-The `._. ` syntax is shorthand for `.provides.`.
8989-9090-## Step 6: Set Global Defaults
9191-9292-Use `den.default` for settings shared across all hosts and users:
9393-9494-```nix
9595-# modules/defaults.nix
9696-{
9797- den.default.homeManager.home.stateVersion = "25.11";
9898- den.default.nixos.system.stateVersion = "25.11";
9999-}
100100-```
101101-102102-## What You've Learned
103103-104104-- [Aspects](/explanation/aspects/) bundle cross-class configs (nixos, darwin, homeManager)
105105-- Host aspects apply to all users on that host
106106-- User aspects apply to all hosts with that user
107107-- `includes` compose aspects together
108108-- `provides` creates nested aspect trees
109109-- [`den.default`](/explanation/context-pipeline/#dendefault-is-an-alias) sets global shared values
110110-111111-## Next
112112-113113-[Context-Aware Configs](/tutorials/context-aware/) — make your aspects
114114-respond dynamically to their host and user context.
···11----
22-title: Getting Started
33-description: Set up your first Den project from scratch.
44----
55-66-## Prerequisites
77-88-- [Nix](https://nixos.org/download) with flakes enabled (or use [without flakes](/guides/no-flakes/))
99-- Basic familiarity with Nix modules
1010-1111-## Quick Start — Launch the Demo VM
1212-1313-Try Den instantly without installing anything:
1414-1515-```console
1616-nix run github:vic/den
1717-```
1818-1919-## Initialize a New Project
2020-2121-Create a fresh Den-based flake:
2222-2323-```console
2424-mkdir my-infra && cd my-infra
2525-nix flake init -t github:vic/den
2626-nix flake update den
2727-```
2828-2929-This creates a project with:
3030-3131-```
3232-flake.nix # inputs and entry point
3333-modules/ # your Den modules go here
3434- hosts.nix # host and user declarations
3535- aspects/ # aspect definitions
3636-```
3737-3838-## Your flake.nix
3939-4040-The generated `flake.nix` imports Den and uses `import-tree` to load all modules:
4141-4242-```nix
4343-{
4444- outputs = inputs:
4545- inputs.flake-parts.lib.mkFlake { inherit inputs; }
4646- (inputs.import-tree ./modules);
4747-4848- inputs = {
4949- den.url = "github:vic/den";
5050- flake-aspects.url = "github:vic/flake-aspects";
5151- import-tree.url = "github:vic/import-tree";
5252- nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
5353- flake-parts.url = "github:hercules-ci/flake-parts";
5454- };
5555-}
5656-```
5757-5858-## Declare a Host
5959-6060-In `modules/hosts.nix`, declare your first host:
6161-6262-```nix
6363-{
6464- den.hosts.x86_64-linux.my-laptop.users.vic = { };
6565-}
6666-```
6767-6868-This single line creates:
6969-- A NixOS host named `my-laptop` on `x86_64-linux`
7070-- A user `vic` with Home-Manager support
7171-- Aspects `den.aspects.my-laptop` and `den.aspects.vic`
7272-7373-## Build It
7474-7575-```console
7676-nixos-rebuild switch --flake .#my-laptop
7777-```
7878-7979-## Available Templates
8080-8181-| Template | Description |
8282-|----------|-------------|
8383-| `default` | Batteries-included layout |
8484-| `minimal` | Minimalistic Den flake |
8585-| `noflake` | No flakes, no flake-parts |
8686-| `example` | Examples and patterns |
8787-| `ci` | Feature test suite |
8888-| `bogus` | Bug reproduction |
8989-9090-```console
9191-nix flake init -t github:vic/den#minimal
9292-```
9393-9494-## Next Steps
9595-9696-- [Your First Aspect](/tutorials/first-aspect/) — write cross-class configs
9797-- [Context-Aware Configs](/tutorials/context-aware/) — make aspects react to context
+118
docs/src/content/docs/tutorials/minimal.md
···11+---
22+title: "Template: Minimal"
33+description: The smallest possible Den setup — one host, one user, no extra dependencies.
44+---
55+66+The minimal template demonstrates Den's core with zero extra dependencies beyond nixpkgs.
77+88+## Initialize
99+1010+```console
1111+mkdir my-nix && cd my-nix
1212+nix flake init -t github:vic/den#minimal
1313+nix flake update den
1414+```
1515+1616+## Project Structure
1717+1818+```
1919+flake.nix
2020+modules/
2121+ den.nix
2222+```
2323+2424+## flake.nix
2525+2626+```nix
2727+{
2828+ outputs = inputs:
2929+ (inputs.nixpkgs.lib.evalModules {
3030+ modules = [ (inputs.import-tree ./modules) ];
3131+ specialArgs = { inherit inputs; };
3232+ }).config.flake;
3333+3434+ inputs = {
3535+ nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
3636+ import-tree.url = "github:vic/import-tree";
3737+ flake-aspects.url = "github:vic/flake-aspects";
3838+ den.url = "github:vic/den";
3939+ };
4040+}
4141+```
4242+4343+Key points:
4444+- **No flake-parts** — uses `lib.evalModules` directly
4545+- **import-tree** — recursively loads all `.nix` files under `modules/`
4646+- **Results** land in `.config.flake` (nixosConfigurations, etc.)
4747+4848+## modules/den.nix
4949+5050+```nix
5151+{ inputs, den, ... }:
5252+{
5353+ imports = [ inputs.den.flakeModule ];
5454+5555+ den.hosts.x86_64-linux.igloo.users.tux = { };
5656+5757+ den.aspects.igloo = {
5858+ nixos = { pkgs, ... }: {
5959+ environment.systemPackages = [ pkgs.hello ];
6060+ boot.loader.grub.enable = false; # TODO: remove for real hardware
6161+ fileSystems."/".device = "/dev/null";
6262+ };
6363+ };
6464+6565+ den.aspects.tux = {
6666+ includes = [ den.provides.primary-user ];
6767+ };
6868+}
6969+```
7070+7171+### What This Declares
7272+7373+1. **One host** named `igloo` on `x86_64-linux` with **one user** named `tux`
7474+2. **Host aspect** `den.aspects.igloo` — provides NixOS config (a package + boot/fs stubs)
7575+3. **User aspect** `den.aspects.tux` — includes the [`primary-user`](/reference/batteries/#primary-user) battery
7676+7777+### How It Works
7878+7979+```mermaid
8080+graph TD
8181+ H["den.hosts.x86_64-linux.igloo"] -->|"ctx.host"| A["den.aspects.igloo"]
8282+ H -->|"ctx.user"| U["den.aspects.tux"]
8383+ U -->|"includes"| PU["den.provides.primary-user"]
8484+ A -->|"resolve nixos"| NixOS["nixosConfigurations.igloo"]
8585+```
8686+8787+1. Den reads `den.hosts` and creates a `ctx.host { host = igloo }` context
8888+2. The host aspect `igloo` is activated with its NixOS config
8989+3. For each user, a `ctx.user { host, user }` context is created
9090+4. The user aspect `tux` includes `primary-user` (adds wheel/networkmanager groups)
9191+5. Everything resolves into `nixosConfigurations.igloo`
9292+9393+## Build It
9494+9595+```console
9696+nix build .#nixosConfigurations.igloo.config.system.build.toplevel
9797+```
9898+9999+## Customize
100100+101101+To add your own host configuration, edit `den.aspects.igloo.nixos`. To add user packages, add a `homeManager` key to `den.aspects.tux` (requires adding `home-manager` input). Or keep it simple — the minimal template works without Home-Manager.
102102+103103+## What It Provides
104104+105105+| Feature | Provided |
106106+|---------|:--------:|
107107+| NixOS host configuration | ✓ |
108108+| User with wheel/networkmanager | ✓ |
109109+| Home-Manager | ✗ |
110110+| Darwin support | ✗ |
111111+| Namespaces | ✗ |
112112+| VM testing | ✗ |
113113+114114+## Next Steps
115115+116116+- Add more aspects in separate files under `modules/`
117117+- Graduate to the [Default template](/tutorials/default/) for Home-Manager support
118118+- Read [Core Principles](/explanation/core-principles/) to understand the context pipeline
+130
docs/src/content/docs/tutorials/noflake.md
···11+---
22+title: "Template: No-Flake"
33+description: Using Den with stable Nix — no flakes needed.
44+---
55+66+The noflake template shows that Den works perfectly without Nix flakes. It uses [npins](https://github.com/andir/npins) for dependency management and works with stable Nix.
77+88+## Initialize
99+1010+Since this template doesn't use flakes, clone it manually:
1111+1212+```console
1313+mkdir my-nix && cd my-nix
1414+cp -r $(nix eval --raw 'github:vic/den#templates.noflake.path')/* .
1515+npins update den
1616+```
1717+1818+Or use `nix flake init` if you have flakes enabled:
1919+2020+```console
2121+nix flake init -t github:vic/den#noflake
2222+```
2323+2424+## Project Structure
2525+2626+```
2727+default.nix # entry point (replaces flake.nix)
2828+with-inputs.nix # input resolver (replaces flake.lock)
2929+npins/
3030+ default.nix # npins fetcher
3131+ sources.json # pinned dependencies
3232+modules/
3333+ den.nix # host/user declarations + aspects
3434+```
3535+3636+## File by File
3737+3838+### default.nix — Entry Point
3939+4040+```nix
4141+let
4242+ outputs = inputs:
4343+ (inputs.nixpkgs.lib.evalModules {
4444+ modules = [ (inputs.import-tree ./modules) ];
4545+ specialArgs = {
4646+ inherit inputs;
4747+ inherit (inputs) self;
4848+ };
4949+ }).config;
5050+in
5151+import ./with-inputs.nix outputs
5252+```
5353+5454+This is the noflake equivalent of `flake.nix`. It uses `lib.evalModules` directly — the same mechanism Den uses internally.
5555+5656+### with-inputs.nix — Input Resolution
5757+5858+This file resolves npins sources into a flake-compatible `inputs` attrset. It reads `flake.nix` from each dependency to discover transitive inputs and wires them together.
5959+6060+### modules/den.nix — Configuration
6161+6262+```nix
6363+{ inputs, ... }:
6464+{
6565+ imports = [ inputs.den.flakeModule ];
6666+6767+ den.default.nixos = {
6868+ fileSystems."/".device = "/dev/fake";
6969+ boot.loader.grub.enable = false;
7070+ };
7171+7272+ den.hosts.x86_64-linux.igloo.users.tux = { };
7373+7474+ den.aspects.igloo = {
7575+ nixos = { pkgs, ... }: {
7676+ environment.systemPackages = [ pkgs.vim ];
7777+ };
7878+ };
7979+8080+ den.aspects.tux = {
8181+ nixos = {
8282+ imports = [ inputs.nix-maid.nixosModules.default ];
8383+ users.users.tux = {
8484+ isNormalUser = true;
8585+ maid.file.home.".gitconfig".text = ''
8686+ [user]
8787+ name=Tux
8888+ '';
8989+ };
9090+ };
9191+ };
9292+}
9393+```
9494+9595+Notable differences from the flake templates:
9696+- Uses [nix-maid](https://github.com/vic/nix-maid) instead of Home-Manager (lighter alternative)
9797+- User config is done directly in `nixos` class (no HM class needed)
9898+9999+## Build
100100+101101+```console
102102+npins update den
103103+nixos-rebuild build --file . -A flake.nixosConfigurations.igloo
104104+```
105105+106106+Or with `nix-build`:
107107+108108+```console
109109+nix-build -A flake.nixosConfigurations.igloo.config.system.build.toplevel
110110+```
111111+112112+:::note
113113+Den places configurations under the `flake` attribute following the flake outputs schema, even without flakes. This is the default top-level attribute for Den output.
114114+:::
115115+116116+## What It Provides
117117+118118+| Feature | Provided |
119119+|---------|:--------:|
120120+| NixOS host configuration | ✓ |
121121+| No flakes required | ✓ |
122122+| npins dependency pinning | ✓ |
123123+| nix-maid (HM alternative) | ✓ |
124124+| Home-Manager | ✗ (use nix-maid) |
125125+| flake-parts | ✗ |
126126+127127+## Next Steps
128128+129129+- Read [Use Without Flakes](/guides/no-flakes/) for more details on flake-free usage
130130+- Consider the [Minimal template](/tutorials/minimal/) if you want flakes but not flake-parts
+61
docs/src/content/docs/tutorials/overview.md
···11+---
22+title: Templates Overview
33+description: All available Den templates and when to use each one.
44+---
55+66+Den ships six templates that cover progressively complex setups. Use `nix flake init` to scaffold a new project:
77+88+```console
99+nix flake init -t github:vic/den#<template>
1010+```
1111+1212+## Choosing a Template
1313+1414+| Template | Use case | Flakes | flake-parts | Home-Manager |
1515+|----------|----------|:------:|:-----------:|:------------:|
1616+| [**minimal**](/tutorials/minimal/) | Smallest possible Den setup | ✓ | ✗ | ✗ |
1717+| [**default**](/tutorials/default/) | Recommended starting point | ✓ | ✓ | ✓ |
1818+| [**example**](/tutorials/example/) | Feature showcase with namespaces | ✓ | ✓ | ✓ |
1919+| [**noflake**](/tutorials/noflake/) | Stable Nix, no flakes | ✗ | ✗ | ✗ |
2020+| [**bogus**](/tutorials/bogus/) | Bug reproduction | ✓ | ✓ | ✓ |
2121+| [**ci**](/tutorials/ci/) | Den's own test suite | ✓ | ✓ | ✓ |
2222+2323+## Quick Start
2424+2525+```console
2626+mkdir my-nix && cd my-nix
2727+nix flake init -t github:vic/den
2828+nix flake update den
2929+```
3030+3131+This clones the **default** template. Edit `modules/hosts.nix` to declare your machines, then:
3232+3333+```console
3434+nix run .#vm
3535+```
3636+3737+## Project Structure
3838+3939+Every template follows the same pattern:
4040+4141+```
4242+flake.nix # or default.nix for noflake
4343+modules/
4444+ den.nix # host/user declarations + den.flakeModule import
4545+ *.nix # aspect definitions, one concern per file
4646+```
4747+4848+Den uses [import-tree](https://github.com/vic/import-tree) to recursively load all `.nix` files under `modules/`. You never need to manually list imports — just create files.
4949+5050+## What Each Template Demonstrates
5151+5252+- **minimal** — The absolute minimum: one host, one user, no extra dependencies
5353+- **default** — Production-ready structure with Home-Manager, VM testing, dendritic flake-file
5454+- **example** — Namespaces, angle brackets, cross-platform (NixOS + Darwin), providers
5555+- **noflake** — Using Den with npins instead of flakes
5656+- **bogus** — Creating minimal reproductions for bug reports with nix-unit
5757+- **ci** — Comprehensive tests covering every Den feature (your best learning resource)
5858+5959+## Next Steps
6060+6161+Start with the [Minimal template](/tutorials/minimal/) to understand Den's core, then graduate to [Default](/tutorials/default/) for a real setup.