Modular, context-aware and aspect-oriented dendritic Nix configurations. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ den.oeiuwq.com
configurations den dendritic nix aspect oriented

Improve Docs (#186)

authored by oeiuwq.com and committed by

GitHub c4ec31cb 0c108ece

+2009 -1269
+3
.github/ISSUE_TEMPLATE/bug_report.md
··· 7 assignees: '' 8 9 --- 10 11 If you have found a bug, please share a reproduction repository with us. 12 ··· 20 ``` 21 22 Your 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. 23 24 ### Description 25
··· 7 assignees: '' 8 9 --- 10 + Please read https://den.oeiuwq.com/tutorials/bogus/ first. 11 12 If you have found a bug, please share a reproduction repository with us. 13 ··· 21 ``` 22 23 Your 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. 24 + 25 + 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. 26 27 ### Description 28
+1 -1
README.md
··· 27 - [Dendritic](https://den.oeiuwq.com/explanation/core-principles/): **same** concern, **different** Nix classes. 28 - [Flake optional](https://den.oeiuwq.com/guides/no-flakes/). Stable/unstable Nix, with/without flake-parts. 29 - [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`. 30 - - Context-aware [dependencies](https://den.oeiuwq.com/explanation/context-system/); `host<->user` [bidirectional](https://den.oeiuwq.com/guides/bidirectional/) contributions. 31 - [Share](https://den.oeiuwq.com/guides/namespaces/) aspects across repos. [Routable](templates/example/modules/aspects/eg/routes.nix) configs. 32 - Custom [factories](https://github.com/vic/den/blob/f5c44098e4855e07bf5cbcec00509e75ddde4220/templates/ci/modules/homes.nix#L20) for any Nix `class`. Per-host `stable`/`unstable` channels. 33 - Freeform [schemas](https://den.oeiuwq.com/reference/schema/) (no `specialArgs`) with [base](https://github.com/vic/den/pull/119) modules.
··· 27 - [Dendritic](https://den.oeiuwq.com/explanation/core-principles/): **same** concern, **different** Nix classes. 28 - [Flake optional](https://den.oeiuwq.com/guides/no-flakes/). Stable/unstable Nix, with/without flake-parts. 29 - [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`. 30 + - Context-aware [dependencies](https://den.oeiuwq.com/explanation/context-system/); `host<->user` [bidirectional](https://den.oeiuwq.com/guides/configure-aspects/) contributions. 31 - [Share](https://den.oeiuwq.com/guides/namespaces/) aspects across repos. [Routable](templates/example/modules/aspects/eg/routes.nix) configs. 32 - Custom [factories](https://github.com/vic/den/blob/f5c44098e4855e07bf5cbcec00509e75ddde4220/templates/ci/modules/homes.nix#L20) for any Nix `class`. Per-host `stable`/`unstable` channels. 33 - Freeform [schemas](https://den.oeiuwq.com/reference/schema/) (no `specialArgs`) with [base](https://github.com/vic/den/pull/119) modules.
+13 -10
docs/astro.config.mjs
··· 45 ], 46 }, 47 { 48 - label: 'Adopt', 49 items: [ 50 - { label: 'Getting Started', slug: 'tutorials/getting-started' }, 51 - { label: 'Your First Aspect', slug: 'tutorials/first-aspect' }, 52 - { label: 'Context-Aware Configs', slug: 'tutorials/context-aware' }, 53 ], 54 }, 55 { 56 - label: 'How-To Guides', 57 items: [ 58 { label: 'Declare Hosts & Users', slug: 'guides/declare-hosts' }, 59 - { label: 'Bidirectional Dependencies', slug: 'guides/bidirectional' }, 60 { label: 'Home-Manager Integration', slug: 'guides/home-manager' }, 61 { label: 'Use Batteries', slug: 'guides/batteries' }, 62 { label: 'Share with Namespaces', slug: 'guides/namespaces' }, 63 { label: 'Angle Brackets Syntax', slug: 'guides/angle-brackets' }, 64 { label: 'Custom Nix Classes', slug: 'guides/custom-classes' }, 65 - { label: 'Use Without Flakes', slug: 'guides/no-flakes' }, 66 { label: 'Migrate to Den', slug: 'guides/migrate' }, 67 { label: 'Debug Configurations', slug: 'guides/debug' }, 68 ], ··· 73 { label: 'den.ctx', slug: 'reference/ctx' }, 74 { label: 'den.lib', slug: 'reference/lib' }, 75 { label: 'den.aspects', slug: 'reference/aspects' }, 76 - { label: 'Entity Schema', slug: 'reference/schema' }, 77 - { label: 'Batteries', slug: 'reference/batteries' }, 78 - { label: 'Configuration Output', slug: 'reference/output' }, 79 ], 80 }, 81 ],
··· 45 ], 46 }, 47 { 48 + label: 'Templates', 49 items: [ 50 + { label: 'Overview', slug: 'tutorials/overview' }, 51 + { label: 'Minimal', slug: 'tutorials/minimal' }, 52 + { label: 'Default', slug: 'tutorials/default' }, 53 + { label: 'Example', slug: 'tutorials/example' }, 54 + { label: 'No-Flake', slug: 'tutorials/noflake' }, 55 + { label: 'Bug Reproduction', slug: 'tutorials/bogus' }, 56 + { label: 'CI Tests', slug: 'tutorials/ci' }, 57 ], 58 }, 59 { 60 + label: 'Cookbook', 61 items: [ 62 { label: 'Declare Hosts & Users', slug: 'guides/declare-hosts' }, 63 + { label: 'Configure Aspects', slug: 'guides/configure-aspects' }, 64 { label: 'Home-Manager Integration', slug: 'guides/home-manager' }, 65 { label: 'Use Batteries', slug: 'guides/batteries' }, 66 { label: 'Share with Namespaces', slug: 'guides/namespaces' }, 67 { label: 'Angle Brackets Syntax', slug: 'guides/angle-brackets' }, 68 { label: 'Custom Nix Classes', slug: 'guides/custom-classes' }, 69 { label: 'Migrate to Den', slug: 'guides/migrate' }, 70 { label: 'Debug Configurations', slug: 'guides/debug' }, 71 ], ··· 76 { label: 'den.ctx', slug: 'reference/ctx' }, 77 { label: 'den.lib', slug: 'reference/lib' }, 78 { label: 'den.aspects', slug: 'reference/aspects' }, 79 + { label: 'den.base', slug: 'reference/schema' }, 80 + { label: 'den.provides', slug: 'reference/batteries' }, 81 + { label: 'flake.*', slug: 'reference/output' }, 82 ], 83 }, 84 ],
+5 -1
docs/src/content/docs/explanation/aspects.md docs/src/content/docs/explanation/aspects.mdx
··· 3 description: How aspects use the __functor pattern for context-awareness. 4 --- 5 6 7 - > 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) 8 9 ## The __functor Pattern 10
··· 3 description: How aspects use the __functor pattern for context-awareness. 4 --- 5 6 + import { Aside } from '@astrojs/starlight/components'; 7 + 8 9 + <Aside title="Use the Source, Luke" icon="github"> 10 + [`nix/lib.nix`](https://github.com/vic/den/blob/main/nix/lib.nix) · Built on [flake-aspects](https://github.com/vic/flake-aspects) 11 + </Aside> 12 13 ## The __functor Pattern 14
-188
docs/src/content/docs/explanation/context-pipeline.md
··· 1 - --- 2 - title: OS Context Pipeline 3 - description: The complete data flow from host declaration to final configuration. 4 - --- 5 - 6 - > 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) 7 - 8 - ## The NixOS/nix-Darwin Pipeline 9 - 10 - When Den evaluates a host configuration, data flows through a pipeline 11 - of context transformations. Here is the complete picture: 12 - 13 - ```mermaid 14 - graph TD 15 - Host["den.hosts.x86_64-linux.igloo"] 16 - Host -->|"initial context"| CtxHost["ctx.host { host }"] 17 - 18 - CtxHost -->|"conf"| HA["den.aspects.igloo<br/>(fixedTo { host })"] 19 - CtxHost -->|"into.default"| CtxDef1["<b>ctx.default</b> { host }"] 20 - CtxHost -->|"into.user<br/>(per user)"| CtxUser["ctx.user { host, user }"] 21 - CtxHost -->|"into.hm-host<br/>(if HM detected)"| CtxHM["ctx.hm-host { host }"] 22 - 23 - CtxUser -->|"conf"| UA["den.aspects.tux<br/>(fixedTo { host, user })"] 24 - CtxUser -->|"into.default"| CtxDef2["<b>ctx.default</b> { host, user }"] 25 - 26 - CtxHM -->|"conf"| HMmod["Import HM module"] 27 - CtxHM -->|"into.hm-user<br/>(per HM user)"| CtxHMU["ctx.hm-user { host, user }"] 28 - 29 - CtxHMU -->|"forward homeManager<br/>into host"| FW["home-manager.users.tux"] 30 - 31 - CtxDef1 -->|"includes"| DI1["<b>den.default.{class}</b> ()<br/>den.default.includes<br/>(host-context funcs)"] 32 - CtxDef2 -->|"includes"| DI2["den.default.includes<br/>(user-context funcs)"] 33 - ``` 34 - 35 - ## Stage by Stage 36 - 37 - ### 1. Host Entry 38 - 39 - Den reads `den.hosts.x86_64-linux.igloo` and applies the initial context `den.ctx.host`: 40 - 41 - ```nix 42 - den.ctx.host { host = den.hosts.x86_64-linux.igloo; } 43 - ``` 44 - 45 - ### 2. Host Aspect Resolution 46 - 47 - `den.ctx.host.conf` locates `den.aspects.igloo` and fixes it to the host context. 48 - All owned configs and static includes from the host aspect are collected. 49 - 50 - ### 3. Default configs (host-level) 51 - 52 - `den.ctx.host.into.default` produces `{ host }` for `den.ctx.default`, which 53 - activates `den.default.includes` functions matching `{ host, ... }`. 54 - 55 - ### 4. User Enumeration 56 - 57 - `den.ctx.host.into.user` maps over `host.users`, producing one 58 - `den.ctx.user { host, user }` per user. 59 - 60 - ### 5. User Aspect Resolution 61 - 62 - `den.ctx.user.conf` locates both the user's aspect (`den.aspects.tux`) and the 63 - host's aspect, collecting contributions from both directions. 64 - 65 - ### 6. Default configs (user-level) 66 - 67 - `ctx.user.into.default` activates `den.default.includes` again, this time 68 - with `{ host, user }` — functions needing user context now match. 69 - 70 - ### 7. Home-Manager Detection 71 - 72 - `den.ctx.host.into.hm-host` checks if the host has users with `homeManager` 73 - class and a supported OS. If so, it activates `den.ctx.hm-host`. 74 - 75 - ### 8. HM Module Import (host-level) 76 - 77 - `den.ctx.hm-host.conf` imports the Home-Manager NixOS/Darwin module. 78 - 79 - ### 9. HM User config collection (user-level) 80 - 81 - For each HM user, `ctx.hm-user` uses `den._.forward` to take 82 - `homeManager` class configs and insert them into 83 - `home-manager.users.<name>` on the host. 84 - 85 - ## Home-Manager Detection Criteria 86 - 87 - `ctx.host.into.hm-host` does not always activate. It checks three conditions 88 - (see [`hm-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-os.nix)): 89 - 90 - 1. **OS class is supported** — the host's class is `nixos` or `darwin` 91 - 2. **HM users exist** — at least one user has `class = "homeManager"` 92 - 3. **HM module available** — `inputs.home-manager` exists, or the host has a custom `hm-module` 93 - 94 - All three must be true. Hosts without users, or with only non-HM users, 95 - skip the entire HM pipeline. 96 - 97 - ## Duplication Caveat with den.default 98 - 99 - `den.default` (alias for `den.ctx.default`) is included at **{host}**, **{host, user}**, **{home}** 100 - context stages — once for the host context and once __per each__ user. 101 - 102 - This means: 103 - 104 - - **Owned** configs and **static** includes from `den.default` can appear 105 - multiple times in the final configuration 106 - - For `mkMerge`-compatible options (most NixOS options), this is harmless 107 - - For options of **types.listOf** or **types.package**, you may get duplicate entries. 108 - 109 - To avoid duplication, use `den.lib.take.exactly` to restrict which 110 - context stages a function matches: 111 - 112 - ```nix 113 - den.default.includes = [ 114 - (den.lib.take.exactly ({ host }: { nixos.x = 1; })) 115 - ]; 116 - ``` 117 - 118 - This function runs only in the `{ host }` context, not in `{ host, user }`. 119 - 120 - :::tip 121 - Prefer attaching configurations directly to specific host or user aspects, or use 122 - `den.ctx.host` / `den.ctx.user` includes, rather than overloading `den.default` 123 - for everything. 124 - ::: 125 - 126 - ## Standalone Home-Manager 127 - 128 - For `den.ctx.home`, the pipeline is shorter: 129 - 130 - ```mermaid 131 - graph TD 132 - Home["den.homes.x86_64-linux.tux"] 133 - Home -->|"creates"| CtxHome["ctx.home { home }"] 134 - CtxHome -->|"conf"| HomeA["den.aspects.tux<br/>(fixedTo { home })"] 135 - CtxHome -->|"into.default"| CtxDef["ctx.default { home }"] 136 - ``` 137 - 138 - ## den.default Is an Alias 139 - 140 - `den.default` is an alias for `den.ctx.default`. When you write: 141 - 142 - ```nix 143 - den.default.homeManager.home.stateVersion = "25.11"; 144 - den.default.includes = [ den._.define-user ]; 145 - ``` 146 - 147 - You are actually setting `den.ctx.default.homeManager...` and 148 - `den.ctx.default.includes`. This means `den.default` is a full 149 - context type — it has `conf`, `into`, `includes`, and owned attributes. 150 - 151 - ### How den.default Receives Data 152 - 153 - `host`, `user`, and `home` aspects **do not** include `den.default` directly. 154 - Instead, each context type transforms **into** `default`: 155 - 156 - ```nix 157 - den.ctx.host.into.default = lib.singleton; # passes { host } 158 - den.ctx.user.into.default = lib.singleton; # passes { host, user } 159 - den.ctx.home.into.default = lib.singleton; # passes { home } 160 - ``` 161 - 162 - This means `den.default` is reached through the declarative context 163 - pipeline, not by direct inclusion. The data flowing into `den.default` 164 - is whatever the source context provides. 165 - 166 - ### Best Practices 167 - 168 - `den.default` is useful for global settings like `home.stateVersion`. 169 - However, prefer attaching parametric includes to the appropriate 170 - context type instead: 171 - 172 - | Instead of | Use | 173 - |-----------|-----| 174 - | `den.default.includes = [ hostFunc ]` | `den.ctx.host.includes = [ hostFunc ]` | 175 - | `den.default.includes = [ hmFunc ]` | `den.ctx.hm-host.includes = [ hmFunc ]` | 176 - | `den.default.nixos.x = 1` | `den.ctx.host.nixos.x = 1` | 177 - 178 - ## Why This Design? 179 - 180 - Each context type is **independent** and **composable**. You can: 181 - 182 - - Add new context types without modifying existing ones 183 - - Attach aspects to any stage of the pipeline 184 - - Create custom transformations for domain-specific needs 185 - - Override any built-in context behavior 186 - 187 - The pipeline is not hardcoded — it's declared through `den.ctx` definitions 188 - that you can inspect, extend, and customize.
···
+208
docs/src/content/docs/explanation/context-pipeline.mdx
···
··· 1 + --- 2 + title: OS Context Pipeline 3 + description: The complete data flow from host declaration to final configuration. 4 + --- 5 + 6 + 7 + import { Aside } from '@astrojs/starlight/components'; 8 + 9 + <Aside title="Use the Source, Luke" icon="github"> 10 + [`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) 11 + </Aside> 12 + 13 + ## The NixOS/nix-Darwin Pipeline 14 + 15 + When Den evaluates a host configuration, data flows through a pipeline 16 + of context transformations. Here is the complete picture: 17 + 18 + ```mermaid 19 + graph TD 20 + Host["den.hosts.x86_64-linux.igloo"] 21 + Host -->|"initial context"| CtxHost["ctx.host { host }"] 22 + 23 + CtxHost -->|"_.host"| HA["den.aspects.igloo<br/>(fixedTo { host })"] 24 + CtxHost -->|"into.default"| CtxDef1["<b>ctx.default</b> { host }"] 25 + CtxHost -->|"into.user<br/>(per user)"| CtxUser["ctx.user { host, user }"] 26 + CtxHost -->|"into.hm-host<br/>(if HM detected)"| CtxHM["ctx.hm-host { host }"] 27 + 28 + CtxUser -->|"_.user (self)"| UA["den.aspects.tux<br/>(fixedTo { host, user })"] 29 + CtxHost -->|"_.user (cross)"| XP["host aspect includes<br/>matching { host, user }"] 30 + CtxUser -->|"into.default"| CtxDef2["<b>ctx.default</b> { host, user }"] 31 + 32 + CtxHM -->|"_.hm-host"| HMmod["Import HM module"] 33 + CtxHM -->|"into.hm-user<br/>(per HM user)"| CtxHMU["ctx.hm-user { host, user }"] 34 + 35 + CtxHMU -->|"forward homeManager<br/>into host"| FW["home-manager.users.tux"] 36 + 37 + CtxDef1 -->|"includes"| DI1["<b>den.default.{class}</b> ()<br/>den.default.includes<br/>(host-context funcs)"] 38 + CtxDef2 -->|"includes"| DI2["den.default.includes<br/>(user-context funcs)"] 39 + ``` 40 + 41 + ## Stage by Stage 42 + 43 + ### 1. Host Entry 44 + 45 + Den reads `den.hosts.x86_64-linux.igloo` and applies the initial context `den.ctx.host`: 46 + 47 + ```nix 48 + den.ctx.host { host = den.hosts.x86_64-linux.igloo; } 49 + ``` 50 + 51 + ### 2. Host Aspect Resolution 52 + 53 + `den.ctx.host.provides.host` (aliased as `_.host`) locates `den.aspects.igloo` 54 + and fixes it to the host context. All owned configs and static includes from the 55 + host aspect are collected. 56 + 57 + ### 3. Default configs (host-level) 58 + 59 + `den.ctx.host.into.default` produces `{ host }` for `den.ctx.default`, which 60 + activates `den.default.includes` functions matching `{ host, ... }`. 61 + 62 + ### 4. User Enumeration 63 + 64 + `den.ctx.host.into.user` maps over `host.users`, producing one 65 + `den.ctx.user { host, user }` per user. 66 + 67 + ### 5. User Aspect Resolution 68 + 69 + Two providers contribute to user context: 70 + - `den.ctx.user.provides.user` (self-provider) — locates the user's own aspect (`den.aspects.tux`) 71 + - `den.ctx.host.provides.user` (cross-provider) — the host's contribution, dispatching host aspect includes matching `{ host, user }` 72 + 73 + ### 6. Default configs (user-level) 74 + 75 + `ctx.user.into.default` activates `den.default.includes` again, this time 76 + with `{ host, user }` — functions needing user context now match. 77 + Because `den.ctx.default` was already visited in stage 3, only parametric 78 + includes are dispatched (owned and static configs are **deduplicated**). 79 + 80 + ### 7. Home-Manager Detection 81 + 82 + `den.ctx.host.into.hm-host` checks if the host has users with `homeManager` 83 + class and a supported OS. If so, it activates `den.ctx.hm-host`. 84 + 85 + ### 8. HM Module Import (host-level) 86 + 87 + `den.ctx.hm-host.provides.hm-host` imports the Home-Manager NixOS/Darwin module. 88 + 89 + ### 9. HM User config collection (user-level) 90 + 91 + For each HM user, `ctx.hm-user` uses `den._.forward` to take 92 + `homeManager` class configs and insert them into 93 + `home-manager.users.<name>` on the host. 94 + 95 + ## Home-Manager Detection Criteria 96 + 97 + `ctx.host.into.hm-host` does not always activate. It checks three conditions 98 + (see [`hm-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/home-manager/hm-os.nix)): 99 + 100 + 1. **OS class is supported** — the host's class is `nixos` or `darwin` 101 + 2. **HM users exist** — at least one user has `class = "homeManager"` 102 + 3. **HM module available** — `inputs.home-manager` exists, or the host has a custom `hm-module` 103 + 104 + All three must be true. Hosts without users, or with only non-HM users, skip the entire HM pipeline. 105 + 106 + ## Automatic Deduplication 107 + 108 + `den.default` (alias for `den.ctx.default`) is reached at **`{host}`**, **`{host, user}`**, 109 + and **`{home}`** context stages — once for the host context and once per user. 110 + 111 + Den's `ctxApply` automatically deduplicates includes using a seen-set: 112 + 113 + - **First visit** to a context type: includes **owned + static + parametric** configs via `fixedTo` 114 + - **Subsequent visits**: includes only **parametric** configs via `atLeast` 115 + 116 + Additionally, for each context, two providers are called: 117 + - **Self-provider** (`provides.${name}`) — the target's own aspect lookup 118 + - **Cross-provider** (`source.provides.${target}`) — the source's contribution (if defined) 119 + 120 + ```mermaid 121 + graph TD 122 + H["ctx.host { host }"] -->|"into.default (1st visit)"| D1["ctx.default { host }<br/><b>fixedTo</b>: owned + statics + parametric<br/>+ self-provider + cross-provider"] 123 + H -->|"into.user"| U["ctx.user { host, user }"] 124 + U -->|"into.default (2nd visit)"| D2["ctx.default { host, user }<br/><b>atLeast</b>: parametric only<br/>+ self-provider + cross-provider"] 125 + ``` 126 + 127 + This means: 128 + 129 + - **Owned** configs and **static** includes from `den.default` appear **exactly once** 130 + - **Parametric** functions in `den.default.includes` still run at every stage 131 + (each may match different context shapes) 132 + - Options of `types.package` or `types.listOf` no longer get duplicate entries 133 + 134 + <Aside> 135 + For parametric includes in `den.default.includes`, use `den.lib.take.exactly` 136 + to restrict which context stages a function matches: 137 + 138 + ```nix 139 + den.default.includes = [ den.aspects.foo ]; 140 + den.aspects.foo = take.exactly ({ host }: { nixos.x = 1; }); 141 + ``` 142 + 143 + This function runs only with `{ host }` context, not with `{ host, user }`. 144 + </Aside> 145 + 146 + ## Standalone Home-Manager 147 + 148 + For `den.ctx.home`, the pipeline is shorter: 149 + 150 + ```mermaid 151 + graph TD 152 + Home["den.homes.x86_64-linux.tux"] 153 + Home -->|"creates"| CtxHome["ctx.home { home }"] 154 + CtxHome -->|"_.home"| HomeA["den.aspects.tux<br/>(fixedTo { home })"] 155 + CtxHome -->|"into.default"| CtxDef["ctx.default { home }"] 156 + ``` 157 + 158 + ## den.default Is an Alias 159 + 160 + `den.default` is an alias for `den.ctx.default`. When you write: 161 + 162 + ```nix 163 + den.default.homeManager.home.stateVersion = "25.11"; 164 + den.default.includes = [ den._.define-user ]; 165 + ``` 166 + 167 + You are actually setting `den.ctx.default.homeManager...` and 168 + `den.ctx.default.includes`. This means `den.default` is a full 169 + context type — it has self-named providers, `into`, `includes`, and owned attributes. 170 + 171 + ### How den.default Receives Data 172 + 173 + `host`, `user`, and `home` aspects **do not** include `den.default` directly. 174 + Instead, each context type transforms **into** `default`: 175 + 176 + ```nix 177 + den.ctx.host.into.default = lib.singleton; # passes { host } 178 + den.ctx.user.into.default = lib.singleton; # passes { host, user } 179 + den.ctx.home.into.default = lib.singleton; # passes { home } 180 + ``` 181 + 182 + This means `den.default` is reached through the declarative context 183 + pipeline, not by direct inclusion. The data flowing into `den.default` 184 + is whatever the source context provides. 185 + 186 + ### Best Practices 187 + 188 + `den.default` is useful for global settings like `home.stateVersion`. 189 + However, prefer attaching parametric includes to the appropriate 190 + context type instead: 191 + 192 + | Instead of | Use | 193 + |-----------|-----| 194 + | `den.default.includes = [ hostFunc ]` | `den.ctx.host.includes = [ hostFunc ]` | 195 + | `den.default.includes = [ hmFunc ]` | `den.ctx.hm-host.includes = [ hmFunc ]` | 196 + | `den.default.nixos.x = 1` | `den.ctx.host.nixos.x = 1` | 197 + 198 + ## Why This Design? 199 + 200 + Each context type is **independent** and **composable**. You can: 201 + 202 + - Add new context types without modifying existing ones 203 + - Attach aspects to any stage of the pipeline 204 + - Create custom transformations for domain-specific needs 205 + - Override any built-in context behavior 206 + 207 + The pipeline is not hardcoded — it's declared through `den.ctx` definitions 208 + that you can inspect, extend, and customize.
+42 -22
docs/src/content/docs/explanation/context-system.md docs/src/content/docs/explanation/context-system.mdx
··· 3 description: How den.ctx defines, transforms, and propagates context. 4 --- 5 6 7 - > 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) 8 9 ## What Is a Context? 10 ··· 30 31 ## Context Types: den.ctx 32 33 - Each context type is defined in `den.ctx` with four components: 34 35 ```nix 36 den.ctx.foobar = { 37 - desc = "The {foo, bar} context"; 38 - conf = { foo, bar }: den.aspects.${foo}._.${bar}; 39 includes = [ /* parametric aspects */ ]; 40 into = { 41 baz = { foo, bar }: [{ baz = computeBaz foo bar; }]; 42 }; 43 }; 44 ``` 45 46 | Component | Purpose | 47 |-----------|---------| 48 - | `desc` | Human-readable description | 49 - | `conf` | Given a context, find aspect responsible for configuration | 50 | `includes` | Parametric aspects activated for this context (aspect cutting-point) | 51 | `into` | Transformations fan-out into other context types | 52 ··· 58 aspect = den.ctx.foobar { foo = "hello"; bar = "world"; }; 59 ``` 60 61 - When applied, Den creates a new aspect that includes the following: 62 63 - 1. **owned configs** from the context itself 64 - 2. **main aspect config** via `conf` (e.g., `den.aspects.hello._.world`) 65 - 3. **included configs** — parametric aspects matching this context 66 - 4. **transforms** — calls each `into` function, producing new contexts 67 - 5. **recurses** — applies each produced context through its own pipeline 68 69 ```mermaid 70 graph TD 71 Apply["ctx.foobar { foo, bar }"] 72 - Apply --> Own["Owned configs"] 73 - Apply --> Conf["ctx → find aspect"] 74 - Apply --> Inc["includes → parametric aspects"] 75 - Apply --> Into["into.baz → new baz contexts"] 76 - Into --> Next["ctx.baz { baz }"] 77 - Next --> Own2["...recurse"] 78 ``` 79 80 ## Transformation Types ··· 118 119 ## Contexts as Aspect Cutting-Points 120 121 - Contexts are aspect-like themselves. They have owned configs and `.includes`: 122 123 ```nix 124 den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true; ··· 138 139 ```nix 140 den.ctx.hm-host.into.foo = { host }: [ { foo = host.name; } ]; 141 - den.ctx.foo.conf = { foo }: { funny.names = [ foo ]; }; 142 ``` 143 144 The module system merges these definitions. You can extend the pipeline ··· 199 Create your own for domain-specific pipelines: 200 201 ```nix 202 - den.ctx.greeting.conf = { hello }: 203 { funny.names = [ hello ]; }; 204 205 den.ctx.greeting.into.shout = { hello }: 206 [{ shout = lib.toUpper hello; }]; 207 208 - den.ctx.shout.conf = { shout }: 209 { funny.names = [ shout ]; }; 210 ``` 211
··· 3 description: How den.ctx defines, transforms, and propagates context. 4 --- 5 6 + import { Aside } from '@astrojs/starlight/components'; 7 + 8 9 + <Aside title="Use the Source, Luke" icon="github"> 10 + [`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) 11 + </Aside> 12 13 ## What Is a Context? 14 ··· 34 35 ## Context Types: den.ctx 36 37 + Each context type is defined in `den.ctx`. Context types are 38 + [aspect submodules](/explanation/aspects/) — they inherit `includes`, `provides`, 39 + `description`, and owned freeform configs from `flake-aspects`'s `aspect` type. 40 + **Den adds only the `into` option for context transformations** 41 42 ```nix 43 den.ctx.foobar = { 44 + description = "The {foo, bar} context"; 45 + 46 + # the aspect responsible for configuring the foobar context 47 + provides.foobar = { foo, bar }: den.aspects.${foo}._.${bar}; 48 + 49 + # foobar contributions to the baz context 50 + provides.baz = { baz }: { }; 51 + 52 + # cutting-point for including other aspects 53 includes = [ /* parametric aspects */ ]; 54 + 55 + # context transformations 56 into = { 57 + # how to produce baz context from a foobar one. 58 baz = { foo, bar }: [{ baz = computeBaz foo bar; }]; 59 }; 60 }; 61 ``` 62 63 + 64 | Component | Purpose | 65 |-----------|---------| 66 + | `description` | Human-readable description | 67 + | `provides.${name}` | Self-named provider: given a context, find aspect responsible for configuration | 68 + | `provides.${target}` | Cross-provider: source's contribution to a target context (called per transformation) | 69 | `includes` | Parametric aspects activated for this context (aspect cutting-point) | 70 | `into` | Transformations fan-out into other context types | 71 ··· 77 aspect = den.ctx.foobar { foo = "hello"; bar = "world"; }; 78 ``` 79 80 + When applied, `ctxApply` walks the full `into` graph depth-first, 81 + collecting `(source, ctxDef, ctx)` triples, then deduplicates: 82 83 + 1. **First visit** to a context type — `fixedTo`: owned + static + parametric dispatch 84 + 2. **Subsequent visits** — `atLeast`: parametric dispatch only, **user must take care** 85 + 3. **Self-named provider** (`provides.${name}`) is always called when context is used 86 + 4. **Cross-provider** (`source.provides.${target}`) is called if the source defines one 87 88 ```mermaid 89 graph TD 90 Apply["ctx.foobar { foo, bar }"] 91 + Apply --> Collect["collectPairs: walk into graph"] 92 + Collect --> Dedup["dedupIncludes: seen-set"] 93 + Dedup --> First["1st visit: fixedTo + self + cross"] 94 + Dedup --> Later["Nth visit: atLeast + self + cross"] 95 + First --> Result["merged includes list"] 96 + Later --> Result 97 ``` 98 99 ## Transformation Types ··· 137 138 ## Contexts as Aspect Cutting-Points 139 140 + Since context types **are** aspect submodules, they naturally have owned 141 + configs and `.includes`: 142 143 ```nix 144 den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true; ··· 158 159 ```nix 160 den.ctx.hm-host.into.foo = { host }: [ { foo = host.name; } ]; 161 + den.ctx.foo._.foo = { foo }: { funny.names = [ foo ]; }; 162 ``` 163 164 The module system merges these definitions. You can extend the pipeline ··· 219 Create your own for domain-specific pipelines: 220 221 ```nix 222 + den.ctx.greeting._.greeting = { hello }: 223 { funny.names = [ hello ]; }; 224 225 den.ctx.greeting.into.shout = { hello }: 226 [{ shout = lib.toUpper hello; }]; 227 228 + den.ctx.shout._.shout = { shout }: 229 { funny.names = [ shout ]; }; 230 ``` 231
+2 -4
docs/src/content/docs/explanation/core-principles.md docs/src/content/docs/explanation/core-principles.mdx
··· 29 (hosts, users, homes) and context types (shapes holding them). Den transforms these through 30 a graph of context stages, each producing richer or conditioned data. 31 32 - The key insight: **the flow of data is declared separately from the 33 - configurations it produces**. 34 35 ### 2. Context-Aware Aspects 36 ··· 39 adapts to every host-user combination. A function requiring `{ other }` is 40 not used. 41 42 - The key insight: **the shape of the function arguments determines when 43 - configs are produced, without explicit conditionals**. 44 45 ## Why This Matters 46
··· 29 (hosts, users, homes) and context types (shapes holding them). Den transforms these through 30 a graph of context stages, each producing richer or conditioned data. 31 32 + **the flow of data is declared separately from the configurations it produces**. 33 34 ### 2. Context-Aware Aspects 35 ··· 38 adapts to every host-user combination. A function requiring `{ other }` is 39 not used. 40 41 + **the shape of the function arguments determines when configs are produced, without explicit conditionals**. 42 43 ## Why This Matters 44
+4 -3
docs/src/content/docs/explanation/library-vs-framework.md docs/src/content/docs/explanation/library-vs-framework.mdx
··· 3 description: Understanding Den's dual nature as both a library and a framework. 4 --- 5 6 ## Den the Library 7 8 At its core, Den is a **library** for activating configuration aspects via context transformations. ··· 45 Here is a deployment pipeline example: 46 47 ```nix 48 - den.ctx.deploy.conf = { target }: 49 den.aspects.${target.name}; 50 51 den.ctx.deploy.into.service = { target }: 52 map (s: { service = s; target = target; }) 53 target.services; 54 55 - den.ctx.service.conf = { service, target }: 56 { terraform.resource.${service.name} = { ... }; }; 57 ``` 58 ··· 61 2. Targets enumerate their services 62 3. Each service produces Terraform configurations 63 64 - The same `ctxApply` mechanics — owned configs, `conf` lookup, 65 `into` transformations — drive this pipeline without any OS-specific code. 66 67 You can even design and test custom context flows independently of
··· 3 description: Understanding Den's dual nature as both a library and a framework. 4 --- 5 6 + 7 ## Den the Library 8 9 At its core, Den is a **library** for activating configuration aspects via context transformations. ··· 46 Here is a deployment pipeline example: 47 48 ```nix 49 + den.ctx.deploy._.deploy = { target }: 50 den.aspects.${target.name}; 51 52 den.ctx.deploy.into.service = { target }: 53 map (s: { service = s; target = target; }) 54 target.services; 55 56 + den.ctx.service._.service = { service, target }: 57 { terraform.resource.${service.name} = { ... }; }; 58 ``` 59 ··· 62 2. Targets enumerate their services 63 3. Each service produces Terraform configurations 64 65 + The same `ctxApply` mechanics — owned configs, self-named provider lookup, 66 `into` transformations — drive this pipeline without any OS-specific code. 67 68 You can even design and test custom context flows independently of
+22 -2
docs/src/content/docs/explanation/parametric.md docs/src/content/docs/explanation/parametric.mdx
··· 3 description: How parametric functors enable context forwarding and adaptation. 4 --- 5 6 - > 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) 7 8 ## What Is a Parametric Aspect? 9 ··· 35 Den provides several parametric functors in 36 `den.lib.parametric`. Each of them provides a 37 different `__functor` beaviour. 38 39 ## den.lib.parametric 40 ··· 176 177 ## take.exactly and take.atLeast 178 179 - For individual functions (not whole aspects), use [`den.lib.take`](/reference/lib/#denlibatake): 180 181 182 ```nix
··· 3 description: How parametric functors enable context forwarding and adaptation. 4 --- 5 6 + import { Aside } from '@astrojs/starlight/components'; 7 + 8 + <Aside title="Use the Source, Luke" icon="github"> 9 + [`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) 10 + </Aside> 11 12 ## What Is a Parametric Aspect? 13 ··· 39 Den provides several parametric functors in 40 `den.lib.parametric`. Each of them provides a 41 different `__functor` beaviour. 42 + 43 + <Aside > 44 + Aspects created by Den from your host/user/home definitions are already `parametric`. 45 + They will forward context to their includes functions. 46 + 47 + However for new aspects you define manually, they are not parametric unless you say so. 48 + If you get the following error: 49 + 50 + ```error 51 + error: function 'anonymous lambda' called without required argument 'user' 52 + ``` 53 + 54 + It likely means you are trying to forward context from an aspect that is not `parametric`. 55 + See also [#182](https://github.com/vic/den/discussions/182) 56 + </Aside> 57 + 58 59 ## den.lib.parametric 60 ··· 196 197 ## take.exactly and take.atLeast 198 199 + For individual functions (not whole aspects), use [`den.lib.take`](/reference/lib/#denlibtake): 200 201 202 ```nix
+5 -16
docs/src/content/docs/guides/angle-brackets.md docs/src/content/docs/guides/angle-brackets.mdx
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <Aside type="tip">Source: [`nix/den-brackets.nix`](https://github.com/vic/den/blob/main/nix/den-brackets.nix)</Aside> 9 10 <Aside type="caution">Angle brackets is an experimental, opt-in feature.</Aside> 11 ··· 21 | `<foo/bar>` | `den.aspects.foo.provides.bar` | 22 | `<foo/bar/baz>` | `den.aspects.foo.provides.bar.provides.baz` | 23 | `<ns/tools>` | `den.ful.ns.tools` | 24 - 25 - ## Enable Per-Module 26 - 27 - Bring `__findFile` into scope from module arguments: 28 - 29 - ```nix 30 - { den, __findFile, ... }: { 31 - _module.args.__findFile = den.lib.__findFile; 32 - den.aspects.igloo.includes = [ <den/define-user> ]; 33 - } 34 - ``` 35 36 ## Enable Globally 37 ··· 70 With a namespace `ns` enabled: 71 72 ```nix 73 - { __findFile, ns, ... }: { 74 - ns.moo.silly = true; 75 - # access: 76 - expr = <ns/moo>; # resolves to den.ful.ns.moo 77 } 78 ``` 79
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 + 9 + <Aside title="Use the Source, Luke" icon="github">[`nix/den-brackets.nix`](https://github.com/vic/den/blob/main/nix/den-brackets.nix)</Aside> 10 11 <Aside type="caution">Angle brackets is an experimental, opt-in feature.</Aside> 12 ··· 22 | `<foo/bar>` | `den.aspects.foo.provides.bar` | 23 | `<foo/bar/baz>` | `den.aspects.foo.provides.bar.provides.baz` | 24 | `<ns/tools>` | `den.ful.ns.tools` | 25 26 ## Enable Globally 27 ··· 60 With a namespace `ns` enabled: 61 62 ```nix 63 + { __findFile, ... }: { 64 + ns.moo.silly = true; # write access 65 + expr = <ns/moo>; # read resolves to den.ful.ns.moo 66 } 67 ``` 68
+3 -15
docs/src/content/docs/guides/batteries.md docs/src/content/docs/guides/batteries.mdx
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <Aside type="tip">Source: [`modules/aspects/provides/`](https://github.com/vic/den/tree/main/modules/aspects/provides) · Reference: [Batteries](/reference/batteries/)</Aside> 9 10 ## What Are Batteries? 11 ··· 109 ``` 110 111 This is how Home-Manager integration is implemented internally. 112 - 113 - ## Writing Your Own 114 - 115 - Any aspect can serve as a battery. Publish it in a namespace for others: 116 - 117 - ```nix 118 - { den, ... }: { 119 - den.provides.my-battery = den.lib.parametric { 120 - includes = [ 121 - ({ host, ... }: { nixos.my-setting = host.name; }) 122 - ]; 123 - }; 124 - } 125 - ```
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 + <Aside title="Use the Source, Luke" icon="github"> 9 + [`modules/aspects/provides/`](https://github.com/vic/den/tree/main/modules/aspects/provides) · Reference: [Batteries](/reference/batteries/) 10 + </Aside> 11 12 ## What Are Batteries? 13 ··· 111 ``` 112 113 This is how Home-Manager integration is implemented internally.
-95
docs/src/content/docs/guides/bidirectional.md
··· 1 - --- 2 - title: Bidirectional Dependencies 3 - description: Hosts configure users and users configure hosts — automatically. 4 - --- 5 - 6 - ## Host → User Configuration 7 - 8 - A host aspect's `homeManager` settings apply to **all users** on that host: 9 - 10 - ```nix 11 - den.aspects.igloo.homeManager.programs.direnv.enable = true; 12 - ``` 13 - 14 - If `igloo` has users `tux` and `pingu`, both get direnv enabled. 15 - 16 - This also works with static includes: 17 - 18 - ```nix 19 - den.aspects.igloo.includes = [ 20 - { homeManager.programs.direnv.enable = true; } 21 - ]; 22 - ``` 23 - 24 - And with parametric includes that receive context: 25 - 26 - ```nix 27 - den.aspects.igloo.includes = [ 28 - ({ host, user }: { 29 - homeManager.programs.direnv.enable = true; 30 - }) 31 - ]; 32 - ``` 33 - 34 - ## User → Host Configuration 35 - 36 - A user aspect's `nixos` or `darwin` settings apply to **every host** with that user: 37 - 38 - ```nix 39 - den.aspects.tux.nixos.programs.fish.enable = true; 40 - ``` 41 - 42 - If `tux` is on both `igloo` and `iceberg`, both hosts get fish enabled. 43 - 44 - User includes work the same way: 45 - 46 - ```nix 47 - den.aspects.tux.includes = [ 48 - ({ host, ... }: { 49 - nixos.users.users.tux.description = "Tux on ${host.name}"; 50 - }) 51 - ]; 52 - ``` 53 - 54 - ## Context-Conditional Bidirectional 55 - 56 - Combine host and user context for precise control: 57 - 58 - ```nix 59 - let 60 - git-on-linux = { user, host, ... }: 61 - if !lib.hasSuffix "darwin" host.system 62 - then { homeManager.programs.git.enable = true; } 63 - else { }; 64 - in { 65 - den.aspects.tux.includes = [ git-on-linux ]; 66 - } 67 - ``` 68 - 69 - User `tux` gets git only on Linux hosts, not on Darwin. 70 - 71 - ## How It Works 72 - 73 - ```mermaid 74 - graph TD 75 - HA["den.aspects.igloo (host)"] -->|"homeManager class"| U1["tux Home-Manager"] 76 - HA -->|"homeManager class"| U2["pingu Home-Manager"] 77 - UA["den.aspects.tux (user)"] -->|"nixos class"| H1["igloo NixOS"] 78 - UA -->|"nixos class"| H2["iceberg NixOS"] 79 - ``` 80 - 81 - Den’s [context system](/explanation/context-system/) handles the routing: 82 - - Host aspect configs flow to all users on that host 83 - - User aspect configs flow to all hosts with that user 84 - - Context functions get called with the specific `{ host, user }` pair 85 - 86 - ## The `den.default` Backbone 87 - 88 - Settings in [`den.default`](/explanation/context-pipeline/#dendefault-is-an-alias) apply to **everything** — all hosts and all users: 89 - 90 - ```nix 91 - den.default.homeManager.home.stateVersion = "25.11"; 92 - den.default.includes = [ 93 - ({ host, ... }: { ${host.class}.networking.hostName = host.name; }) 94 - ]; 95 - ```
···
+136
docs/src/content/docs/guides/configure-aspects.mdx
···
··· 1 + --- 2 + title: Configure Aspects 3 + description: Recipes for configuring aspects — owned configs, includes, providers, and bidirectional dependencies. 4 + --- 5 + 6 + import { Aside } from '@astrojs/starlight/components'; 7 + 8 + <Aside title="Use the Source, Luke" icon="github"> 9 + [`modules/aspects/`](https://github.com/vic/den/tree/main/modules/aspects) · Tests: [`features/user-host-bidirectional-config.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/user-host-bidirectional-config.nix) 10 + </Aside> 11 + 12 + ## Static Host Config 13 + 14 + Set NixOS/Darwin/HM options directly on an aspect: 15 + 16 + ```nix 17 + den.aspects.igloo = { 18 + nixos = { pkgs, ... }: { 19 + environment.systemPackages = [ pkgs.hello ]; 20 + }; 21 + homeManager.programs.vim.enable = true; 22 + }; 23 + ``` 24 + 25 + ## Include Other Aspects 26 + 27 + Compose aspects with `includes`: 28 + 29 + ```nix 30 + den.aspects.tux = { 31 + includes = [ 32 + den.provides.primary-user 33 + (den.provides.user-shell "fish") 34 + den.aspects.base-tools 35 + ]; 36 + }; 37 + ``` 38 + 39 + ## Context-Aware Includes 40 + 41 + Functions in `includes` receive context and only activate when they match: 42 + 43 + ```nix 44 + den.aspects.igloo.includes = [ 45 + den.aspects.video 46 + ]; 47 + 48 + den.aspects.video = den.lib.take.exactly ({ host, user }: { 49 + nixos.users.users.${user.userName}.extraGroups = [ "video" ]; 50 + }); 51 + 52 + ``` 53 + 54 + This runs **once per user** on the host. A function taking `{ host }` only runs at the host level. 55 + 56 + ## Host Configures Its Users 57 + 58 + A host aspect can contribute to user-level settings: 59 + 60 + ```nix 61 + den.aspects.igloo = { 62 + nixos.networking.hostName = "igloo"; 63 + homeManager.programs.direnv.enable = true; 64 + }; 65 + ``` 66 + 67 + The `homeManager` config from a host aspect becomes the default for **all users** on that host. 68 + 69 + ## User Configures Its Host 70 + 71 + A user aspect can contribute to the host's NixOS/Darwin config: 72 + 73 + ```nix 74 + den.aspects.tux = { 75 + nixos.users.users.tux.description = "cute penguin"; 76 + homeManager = { pkgs, ... }: { 77 + home.packages = [ pkgs.htop ]; 78 + }; 79 + }; 80 + ``` 81 + 82 + The `nixos` config from a user aspect is applied to **every host** the user appears on. 83 + 84 + 85 + ## Conditional Config by Host/User 86 + 87 + Use aspect arguments to specialize: 88 + 89 + ```nix 90 + den.aspects.igloo.includes = [ den.aspects.tuxGit ]; 91 + 92 + den.aspects.tuxGit = den.lib.take.exactly ({ host, user }: 93 + if user.userName == "tux" 94 + then { homeManager.programs.git.enable = true; } 95 + else { } 96 + ); 97 + ``` 98 + 99 + ## Global Defaults 100 + 101 + Use `den.default` for settings that apply everywhere: 102 + 103 + ```nix 104 + den.default = { 105 + nixos.system.stateVersion = "25.11"; 106 + homeManager.home.stateVersion = "25.11"; 107 + includes = [ 108 + den.provides.define-user 109 + (den.lib.take.exactly ({ host }: { 110 + nixos.networking.hostName = host.hostName; 111 + })) 112 + ]; 113 + }; 114 + ``` 115 + 116 + Use `den.lib.take.exactly` to restrict a function to a specific context shape (e.g., only at host level, not user level). 117 + 118 + ## Compose Named Sub-aspects 119 + 120 + Organize aspects hierarchically using `provides`: 121 + 122 + ```nix 123 + den.aspects.gaming = { 124 + nixos.programs.steam.enable = true; 125 + provides.emulation.nixos.programs.retroarch.enable = true; 126 + provides.streaming = { 127 + nixos.services.sunshine.enable = true; 128 + homeManager.programs.moonlight.enable = true; 129 + }; 130 + }; 131 + 132 + den.aspects.my-pc.includes = [ 133 + den.aspects.gaming 134 + den.aspects.gaming._.emulation 135 + ]; 136 + ```
+13 -30
docs/src/content/docs/guides/custom-classes.md docs/src/content/docs/guides/custom-classes.mdx
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <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> 9 10 ## What Are Custom Classes? 11 ··· 20 21 ## Example: Custom Class to NixOS 22 23 - Forward a `custom` class into NixOS top-level: 24 25 ```nix 26 { den, lib, ... }: 27 let 28 - forwarded = { class, aspect-chain }: 29 den._.forward { 30 - each = lib.singleton class; 31 - fromClass = _: "custom"; 32 - intoClass = _: "nixos"; 33 - intoPath = _: [ ]; 34 - fromAspect = _: lib.head aspect-chain; 35 }; 36 in { 37 - den.aspects.igloo = { 38 - includes = [ forwarded ]; 39 - custom.networking.hostName = "from-custom-class"; 40 - }; 41 } 42 ``` 43 44 - ## Example: Forward into a Subpath 45 - 46 - Insert configs into a nested submodule: 47 - 48 - ```nix 49 - den.aspects.igloo = { 50 - includes = [ forwarded ]; 51 - nixos.imports = [ 52 - { options.fwd-box = lib.mkOption { 53 - type = lib.types.submoduleWith { modules = [ myModule ]; }; 54 - }; } 55 - ]; 56 - src.items = [ "from-src-class" ]; 57 - }; 58 - ``` 59 - 60 - With `intoPath = _: [ "fwd-box" ]`, the `src` class configs merge into 61 - `nixos.fwd-box`. 62 63 ## How Home-Manager Uses Forward 64
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 + <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> 9 10 ## What Are Custom Classes? 11 ··· 20 21 ## Example: Custom Class to NixOS 22 23 + Forward a new `hjem` class into NixOS top-level: 24 25 ```nix 26 { den, lib, ... }: 27 let 28 + hjem = { host }: { class, aspect-chain }: 29 den._.forward { 30 + each = host.users; 31 + fromClass = user: "hjem"; 32 + intoClass = user: class; 33 + intoPath = user: [ "hjem" "users" user.userName ] 34 + fromAspect = user: den.aspects.${user.aspect}; 35 }; 36 in { 37 + # host forwards user hjem modules into nixos level. 38 + den.aspects.igloo = { includes = [ hjem ]; }; 39 + 40 + # user aspect has hjem class 41 + den.aspects.tux.hjem = { ... }; 42 } 43 ``` 44 45 46 ## How Home-Manager Uses Forward 47
+4 -11
docs/src/content/docs/guides/debug.md
··· 25 }); 26 ``` 27 28 - ## Trace Context Keys 29 - 30 - See which contexts are being applied: 31 - 32 - ```nix 33 - den.default.includes = [ 34 - (context: builtins.trace (builtins.attrNames context) { }) 35 - ]; 36 - ``` 37 38 ## REPL Inspection 39 ··· 89 90 ## Common Issues 91 92 - **Duplicate values in lists**: Your function matches too many contexts. 93 - Use `den.lib.take.exactly` to restrict matching: 94 95 ```nix 96 den.lib.take.exactly ({ host }: { nixos.x = 1; })
··· 25 }); 26 ``` 27 28 29 ## REPL Inspection 30 ··· 80 81 ## Common Issues 82 83 + **Duplicate values in lists**: Den automatically deduplicates owned and 84 + static configs from `den.default`, but parametric functions in 85 + `den.default.includes` still run at every context stage. Use 86 + `den.lib.take.exactly` to restrict matching: 87 88 ```nix 89 den.lib.take.exactly ({ host }: { nixos.x = 1; })
+13 -13
docs/src/content/docs/guides/declare-hosts.md docs/src/content/docs/guides/declare-hosts.mdx
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <Aside type="tip">Source: [`modules/_types.nix`](https://github.com/vic/den/blob/main/modules/_types.nix) · Reference: [Entity Schema](/reference/schema/)</Aside> 9 10 ## Hosts and Users 11 ··· 16 den.hosts.aarch64-darwin.macbook.users.vic = { }; 17 ``` 18 19 - This creates NixOS/Darwin configurations with users, ready for: 20 21 ```console 22 nixos-rebuild switch --flake .#my-laptop ··· 74 ```nix 75 den.hosts.x86_64-linux.my-laptop = { 76 isWarm = true; # custom attribute 77 - location = "home"; # custom attribute 78 users.vic = { }; 79 }; 80 ``` ··· 103 104 ## Custom Instantiation 105 106 - Override how configurations are built: 107 108 ```nix 109 - den.hosts.x86_64-linux.wsl-box = { 110 - class = "nixos"; 111 - instantiate = inputs.nixos-wsl.lib.nixosSystem; 112 - intoAttr = "wslConfigurations"; 113 - users.vic = { }; 114 }; 115 ``` 116 117 - Different `nixpkgs` channels per host: 118 119 ```nix 120 - den.hosts.x86_64-linux.stable-server = { 121 - instantiate = inputs.nixpkgs-stable.lib.nixosSystem; 122 - users.admin = { }; 123 }; 124 ```
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 + <Aside title="Use the Source, Luke" icon="github"> 9 + [`modules/_types.nix`](https://github.com/vic/den/blob/main/modules/_types.nix) · Reference: [Entity Schema](/reference/schema/) 10 + </Aside> 11 12 ## Hosts and Users 13 ··· 18 den.hosts.aarch64-darwin.macbook.users.vic = { }; 19 ``` 20 21 + This creates NixOS/Darwin configurations with users sharing the same home configuration, ready for: 22 23 ```console 24 nixos-rebuild switch --flake .#my-laptop ··· 76 ```nix 77 den.hosts.x86_64-linux.my-laptop = { 78 isWarm = true; # custom attribute 79 + location = "studio"; # custom attribute 80 users.vic = { }; 81 }; 82 ``` ··· 105 106 ## Custom Instantiation 107 108 + Different `nixpkgs` channels per host: 109 110 ```nix 111 + den.hosts.x86_64-linux.stable-server = { 112 + instantiate = inputs.nixpkgs-stable.lib.nixosSystem; 113 + users.admin = { }; 114 }; 115 ``` 116 117 + Override where configurations are placed: 118 119 ```nix 120 + den.hosts.x86_64-linux.wsl-box = { 121 + intoAttr = "wslConfigurations"; 122 + users.wsl = { }; 123 }; 124 ```
+4 -1
docs/src/content/docs/guides/home-manager.md docs/src/content/docs/guides/home-manager.mdx
··· 3 description: Integrate Home-Manager into NixOS/Darwin hosts or use standalone. 4 --- 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <Aside type="tip">Source: [`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)</Aside> 9 10 ## Automatic HM Detection 11
··· 3 description: Integrate Home-Manager into NixOS/Darwin hosts or use standalone. 4 --- 5 6 + 7 import { Aside } from '@astrojs/starlight/components'; 8 9 + <Aside title="Use the Source, Luke" icon="github"> 10 + [`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) 11 + </Aside> 12 13 ## Automatic HM Detection 14
+15 -5
docs/src/content/docs/guides/namespaces.md docs/src/content/docs/guides/namespaces.mdx
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <Aside type="tip">Source: [`nix/namespace.nix`](https://github.com/vic/den/blob/main/nix/namespace.nix)</Aside> 9 10 ## What Are Namespaces? 11 12 Namespaces let you organize aspects into named collections that can be 13 shared across flakes and consumed by others. 14 15 ## Define a Local Namespace 16 17 ```nix ··· 21 } 22 ``` 23 24 The second argument controls output: 25 - `false` — local only, not exposed as flake output 26 - `true` — exposed at `flake.denful.ns` ··· 28 29 ## Consume Remote Namespaces 30 31 - Import aspects from another flake's `denful` output: 32 33 ```nix 34 { inputs, ... }: { ··· 40 } 41 ``` 42 43 - Multiple sources are merged. Local definitions override remote ones. 44 45 ## Nested Provides in Namespaces 46 ··· 92 angle brackets: 93 94 ```nix 95 - { __findFile, ns, ... }: { 96 _module.args.__findFile = den.lib.__findFile; 97 den.aspects.igloo.includes = [ <ns/tools> ]; 98 } 99 ``` 100 101 ## Real-World: denful 102 103 - [denful](https://github.com/vic/denful) is a community aspect distribution 104 built on Den namespaces — a lazyvim-like approach to Nix configurations.
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 + <Aside title="Use the Source, Luke" icon="github">[`nix/namespace.nix`](https://github.com/vic/den/blob/main/nix/namespace.nix)</Aside> 9 10 ## What Are Namespaces? 11 12 Namespaces let you organize aspects into named collections that can be 13 shared across flakes and consumed by others. 14 15 + This is unlike your `den.aspects.<name>` namespace which is private, 16 + because you wont likely want to share your user/host configurations. 17 + 18 + Namespaces are for re-usable aspects you are willing to share with others. 19 + 20 ## Define a Local Namespace 21 22 ```nix ··· 26 } 27 ``` 28 29 + The first argument is the aspect namespace name. 30 + It will be provided as module argument: `{ lib, ns, ... }` 31 + so you can access aspects from the namespace. 32 + 33 The second argument controls output: 34 - `false` — local only, not exposed as flake output 35 - `true` — exposed at `flake.denful.ns` ··· 37 38 ## Consume Remote Namespaces 39 40 + Import aspects from another flake's `flake.denful.provider` output: 41 42 ```nix 43 { inputs, ... }: { ··· 49 } 50 ``` 51 52 + Multiple sources are merged by the module system. 53 54 ## Nested Provides in Namespaces 55 ··· 101 angle brackets: 102 103 ```nix 104 + { __findFile, ... }: { 105 _module.args.__findFile = den.lib.__findFile; 106 + 107 den.aspects.igloo.includes = [ <ns/tools> ]; 108 } 109 ``` 110 111 ## Real-World: denful 112 113 + [denful](https://github.com/vic/denful) is a (WIP) community aspect distribution 114 built on Den namespaces — a lazyvim-like approach to Nix configurations.
-91
docs/src/content/docs/guides/no-flakes.md
··· 1 - --- 2 - title: Use Without Flakes 3 - description: Den works with stable Nix, npins, and without flake-parts. 4 - --- 5 - 6 - ## Den is Flake-Agnostic 7 - 8 - Den does **not** require Nix flakes. It works with: 9 - 10 - - Nix flakes + flake-parts (most common) 11 - - Nix flakes without flake-parts 12 - - No flakes at all (stable Nix + npins or fetchTarball) 13 - 14 - ## Without Flakes 15 - 16 - Use `flake-compat` to evaluate Den from a non-flake setup: 17 - 18 - ```nix 19 - # default.nix 20 - let 21 - flake = import (fetchTarball { 22 - url = "https://github.com/edolstra/flake-compat/archive/...tar.gz"; 23 - }) { src = ./.; }; 24 - in 25 - flake.outputs 26 - ``` 27 - 28 - See the [`noflake` template](https://github.com/vic/den/tree/main/templates/noflake) 29 - for a complete working example with npins. 30 - 31 - ## Without Flake-Parts 32 - 33 - Den provides its own minimal `flake` option when flake-parts is not present. 34 - Simply import `inputs.den.flakeModule`: 35 - 36 - ```nix 37 - let 38 - denCfg = (lib.evalModules { 39 - modules = [ 40 - (import-tree ./modules) 41 - inputs.den.flakeModule 42 - ]; 43 - specialArgs = { inherit inputs; }; 44 - }).config; 45 - in { 46 - nixosConfigurations.igloo = 47 - denCfg.flake.nixosConfigurations.igloo; 48 - } 49 - ``` 50 - 51 - ## With Flake-Parts 52 - 53 - The standard setup — Den integrates seamlessly: 54 - 55 - ```nix 56 - { 57 - outputs = inputs: 58 - inputs.flake-parts.lib.mkFlake { inherit inputs; } 59 - (inputs.import-tree ./modules); 60 - 61 - inputs = { 62 - den.url = "github:vic/den"; 63 - flake-aspects.url = "github:vic/flake-aspects"; 64 - import-tree.url = "github:vic/import-tree"; 65 - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 66 - flake-parts.url = "github:hercules-ci/flake-parts"; 67 - }; 68 - } 69 - ``` 70 - 71 - ## The Dendritic Module 72 - 73 - For flake-parts users who want automatic Den + flake-aspects setup: 74 - 75 - ```nix 76 - imports = [ inputs.den.flakeModules.dendritic ]; 77 - ``` 78 - 79 - This auto-configures `flake-file` inputs for `den` and `flake-aspects`. 80 - 81 - ## Minimal Dependencies 82 - 83 - Den's only hard dependency is `flake-aspects`. Everything else is optional: 84 - 85 - | Dependency | Required? | Purpose | 86 - |-----------|-----------|---------| 87 - | `flake-aspects` | **Yes** | [Aspect composition](https://github.com/vic/flake-aspects) | 88 - | `import-tree` | Optional | [Auto-import module directories](https://github.com/vic/import-tree) | 89 - | `flake-parts` | Optional | Flake structuring | 90 - | `nixpkgs` | Optional | Only if building NixOS/HM configs | 91 - | `home-manager` | Optional | Only if using HM integration |
···
+3 -3
docs/src/content/docs/index.mdx
··· 9 <img width="400" height="400" src="https://github.com/user-attachments/assets/af9c9bca-ab8b-4682-8678-31a70d510bbb" /> 10 actions: 11 - text: NixOS Framework 12 - link: /tutorials/getting-started/ 13 icon: right-arrow 14 - text: Pure Nix Library 15 link: /explanation/library-vs-framework ··· 137 # generic config for all hosts, users, homes. 138 den.default.nixos.system.stateVersion = "25.11"; 139 den.default.homeManager.home.stateVersion = "25.11"; 140 - den.default.includes [ den._.inputs' ]; 141 142 } 143 ``` ··· 150 </Card> 151 <Card title="Bidirectional" icon="right-arrow"> 152 Hosts configure their users. Users contribute to their 153 - hosts. [Aspects flow in both directions](/guides/bidirectional/) automatically. 154 </Card> 155 <Card title="Batteries Included" icon="add-document"> 156 Opt-in [aspects for common tasks](/guides/batteries/): define-user, primary-user,
··· 9 <img width="400" height="400" src="https://github.com/user-attachments/assets/af9c9bca-ab8b-4682-8678-31a70d510bbb" /> 10 actions: 11 - text: NixOS Framework 12 + link: /tutorials/overview/ 13 icon: right-arrow 14 - text: Pure Nix Library 15 link: /explanation/library-vs-framework ··· 137 # generic config for all hosts, users, homes. 138 den.default.nixos.system.stateVersion = "25.11"; 139 den.default.homeManager.home.stateVersion = "25.11"; 140 + den.default.includes = [ den._.inputs' ]; 141 142 } 143 ``` ··· 150 </Card> 151 <Card title="Bidirectional" icon="right-arrow"> 152 Hosts configure their users. Users contribute to their 153 + hosts. [Aspects flow in both directions](/guides/configure-aspects/) automatically. 154 </Card> 155 <Card title="Batteries Included" icon="add-document"> 156 Opt-in [aspects for common tasks](/guides/batteries/): define-user, primary-user,
+20 -12
docs/src/content/docs/motivation.md docs/src/content/docs/motivation.mdx
··· 3 description: The motivation and fundamental idea behind Den. 4 --- 5 6 7 - > This section is about how and why Den came to be. 8 - > You can safely skip this unless you are interested in how Den was shaped. 9 - > 10 - > However you might need to already be familiar with what [Dendritic](https://github.com/mightyiam/dendritic) means 11 - > you might also want to read the [FAQ](https://github.com/Doc-Steve/dendritic-design-with-flake-parts/wiki/FAQ) 12 13 Den is basically about a single idea: 14 15 - > Being able to share re-usable (parametric) cross-class Nix configurations. 16 17 Den was born not to be yet-another zillionth way to wire-up configurations. 18 ··· 32 At 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. 33 34 However, 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. 35 - 36 - > The problem was more about feature composability. 37 38 Being an fan of functional programming, the most composable things I know are functions. 39 ··· 59 An 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. 60 61 62 - > 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. 63 64 ## Den builds upon flake-aspects 65 ··· 77 78 `den.default.includes = [ (den._.unfree ["vscode"]) ]` 79 80 - - How a `Host` can affect its `User`s configurations, and [viceversa](/guides/bidirectional). 81 82 - How to [mixin](/guides/namespaces) aspects from remote sources and enhance locally defined ones. 83 ··· 98 99 Den is more about giving to others (creating useful Nix software/configurations), not just about make things work locally. 100 101 - > 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) 102 103 104 ## What's Next? 105 106 - Ready to try it? Head to the [Getting Started](/tutorials/getting-started/) tutorial. 107 108 Want to understand the architecture? Read about the [Core Principles](/explanation/core-principles/).
··· 3 description: The motivation and fundamental idea behind Den. 4 --- 5 6 + import { Aside } from '@astrojs/starlight/components'; 7 8 + <Aside> 9 + This section is about how and why Den came to be. 10 + You can safely skip this unless you are interested in how Den was shaped. 11 + 12 + However you might need to already be familiar with what [Dendritic](https://github.com/mightyiam/dendritic) means 13 + you might also want to read the [FAQ](https://github.com/Doc-Steve/dendritic-design-with-flake-parts/wiki/FAQ) 14 + </Aside> 15 16 Den is basically about a single idea: 17 18 + <Aside type="tip" icon="star" title="Motivation"> 19 + Den was built for being able to share re-usable (parametric) cross-class Nix configurations. 20 + </Aside> 21 22 Den was born not to be yet-another zillionth way to wire-up configurations. 23 ··· 37 At 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. 38 39 However, 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. 40 + **The problem was more about feature composability.** 41 42 Being an fan of functional programming, the most composable things I know are functions. 43 ··· 63 An 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. 64 65 66 + <Aside> 67 + 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. 68 + </Aside> 69 70 ## Den builds upon flake-aspects 71 ··· 83 84 `den.default.includes = [ (den._.unfree ["vscode"]) ]` 85 86 + - How a `Host` can affect its `User`s configurations, and [viceversa](/guides/configure-aspects). 87 88 - How to [mixin](/guides/namespaces) aspects from remote sources and enhance locally defined ones. 89 ··· 104 105 Den is more about giving to others (creating useful Nix software/configurations), not just about make things work locally. 106 107 + <Aside type="tip" title="Support Den development" icon="heart"> 108 + 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) 109 + </Aside> 110 111 112 ## What's Next? 113 114 + Ready to try it? Head to the [Templates overview](/tutorials/overview/) to pick a starting point. 115 116 Want to understand the architecture? Read about the [Core Principles](/explanation/core-principles/).
-127
docs/src/content/docs/reference/aspects.md
··· 1 - --- 2 - title: den.aspects Reference 3 - description: Aspect structure, resolution, and configuration. 4 - --- 5 - 6 - import { Aside } from '@astrojs/starlight/components'; 7 - 8 - <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> 9 - 10 - ## Aspect Attributes 11 - 12 - Every aspect is an attribute set with these recognized keys: 13 - 14 - | Attribute | Type | Description | 15 - |-----------|------|-------------| 16 - | `nixos` | module | NixOS configuration module | 17 - | `darwin` | module | nix-darwin configuration module | 18 - | `homeManager` | module | Home-Manager configuration module | 19 - | `<class>` | module | Any custom Nix class | 20 - | `includes` | list | Dependencies on other aspects or functions | 21 - | `provides` | attrset | Nested sub-aspects | 22 - | `_` | alias | Shorthand for `provides` | 23 - | `description` | str | Human-readable description | 24 - | `__functor` | function | Context-aware behavior | 25 - 26 - ## Automatic Aspect Creation 27 - 28 - Den creates an aspect for each host, user, and home you declare: 29 - 30 - ```nix 31 - den.hosts.x86_64-linux.igloo.users.tux = { }; 32 - # Creates: den.aspects.igloo and den.aspects.tux 33 - ``` 34 - 35 - Aspects are created with `parametric { <class> = {}; }` functor 36 - matching the entity's class. 37 - 38 - ## Aspect Resolution 39 - 40 - To extract a class module from an aspect: 41 - 42 - ```nix 43 - module = aspect.resolve { class = "nixos"; aspect-chain = []; }; 44 - ``` 45 - 46 - Resolution collects the specified class from the aspect and all 47 - transitive includes into a single merged Nix module. 48 - 49 - ## den.aspects (option) 50 - 51 - ```nix 52 - options.den.aspects = lib.mkOption { 53 - type = aspectsType; 54 - default = { }; 55 - }; 56 - ``` 57 - 58 - Contributions from any module are merged: 59 - 60 - ```nix 61 - # file1.nix 62 - den.aspects.igloo.nixos.networking.hostName = "igloo"; 63 - 64 - # file2.nix 65 - den.aspects.igloo.homeManager.programs.vim.enable = true; 66 - ``` 67 - 68 - ## den.provides (batteries) 69 - 70 - ```nix 71 - options.den.provides = lib.mkOption { 72 - type = lib.types.submodule { 73 - freeformType = lib.types.attrsOf providerType; 74 - }; 75 - }; 76 - ``` 77 - 78 - Access via `den._.name` or `den.provides.name`. 79 - See [Batteries Reference](/reference/batteries/) for all built-in aspects. 80 - 81 - ## den.default 82 - 83 - The global dispatcher aspect: 84 - 85 - ```nix 86 - den.default = den.lib.parametric.atLeast { }; 87 - ``` 88 - 89 - All hosts, users, and homes include `den.default`. Set global 90 - configs and shared [parametric](/explanation/parametric/) includes here. 91 - 92 - Aliased from `den.ctx.default`. 93 - 94 - ## Including Aspects 95 - 96 - ```nix 97 - den.aspects.igloo.includes = [ 98 - # another aspect 99 - den.aspects.gaming 100 - 101 - # nested provides 102 - den.aspects.tools._.editors 103 - 104 - # static config 105 - { homeManager.programs.direnv.enable = true; } 106 - 107 - # context function 108 - ({ host, ... }: { nixos.time.timeZone = "UTC"; }) 109 - 110 - # battery 111 - den._.define-user 112 - 113 - # battery with args 114 - (den._.user-shell "fish") 115 - (den._.unfree [ "discord" ]) 116 - ]; 117 - ``` 118 - 119 - ## Aspect as Module Argument 120 - 121 - `den` is available as a module argument in all Den modules: 122 - 123 - ```nix 124 - { den, ... }: { 125 - den.aspects.foo = den.lib.parametric { ... }; 126 - } 127 - ```
···
+74
docs/src/content/docs/reference/aspects.mdx
···
··· 1 + --- 2 + title: den.aspects Reference 3 + description: Aspect structure, resolution, and configuration. 4 + --- 5 + 6 + import { Aside } from '@astrojs/starlight/components'; 7 + 8 + <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> 9 + 10 + ## Aspect Attributes 11 + 12 + Every aspect is an attribute set with these recognized keys: 13 + 14 + | Attribute | Type | Description | 15 + |-----------|------|-------------| 16 + | `description` | str | Human-readable description | 17 + | `__functor` | function | Context-aware behavior | 18 + | `includes` | list | Dependencies on other aspects or functions | 19 + | `provides` | attrset | Nested sub-aspects | 20 + | `_` | alias | Shorthand for `provides` | 21 + | `<class>` | module | Any Nix class, like the following | 22 + | `nixos` | module | NixOS configuration module | 23 + | `darwin` | module | nix-darwin configuration module | 24 + | `homeManager` | module | Home-Manager configuration module | 25 + 26 + ## Automatic Aspect Creation 27 + 28 + Den creates an aspect for each host, user, and home you declare: 29 + 30 + ```nix 31 + den.hosts.x86_64-linux.igloo.users.tux = { }; 32 + 33 + # will create: 34 + 35 + den.aspects.igloo = parametric { }; 36 + den.aspects.tux = parametric { }; 37 + ``` 38 + 39 + ## Aspect Resolution 40 + 41 + To extract a class module from an aspect use the flake-aspects API: 42 + 43 + ```nix 44 + module = aspect.resolve { class = "nixos"; aspect-chain = []; }; 45 + ``` 46 + 47 + Resolution collects the specified class from the aspect and all 48 + transitive includes into a single merged Nix module. 49 + 50 + ## den.aspects (namespace) 51 + 52 + This namespace is where Den creates aspects for your host/user/home. 53 + 54 + You can also use this namespace to create your own aspects. 55 + Aspects in this namespace are not shared outside the flake, 56 + for that use `den.namespace`. 57 + 58 + Contributions from any module are merged: 59 + 60 + ```nix 61 + # file1.nix 62 + den.aspects.igloo.nixos.networking.hostName = "igloo"; 63 + 64 + # file2.nix 65 + den.aspects.igloo.homeManager.programs.vim.enable = true; 66 + ``` 67 + 68 + ## den.provides (namespace) 69 + 70 + Den batteries-included re-usable aspects. 71 + 72 + Access via `den._.name` or `den.provides.name`. 73 + 74 + See [Batteries Reference](/reference/batteries/) for all built-in aspects.
+12 -7
docs/src/content/docs/reference/batteries.md docs/src/content/docs/reference/batteries.mdx
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <Aside type="tip">Source: [`modules/aspects/provides/`](https://github.com/vic/den/tree/main/modules/aspects/provides)</Aside> 9 10 ## Overview 11 ··· 15 ## define-user 16 17 Defines a user at OS and Home-Manager levels. 18 - ([source](https://github.com/vic/den/blob/main/modules/aspects/provides/define-user.nix)) 19 20 ```nix 21 den.default.includes = [ den._.define-user ]; ··· 31 ## primary-user 32 33 Makes a user an administrator. 34 - ([source](https://github.com/vic/den/blob/main/modules/aspects/provides/primary-user.nix)) 35 36 ```nix 37 den.aspects.vic.includes = [ den._.primary-user ]; ··· 47 ## user-shell 48 49 Sets default shell at OS and HM levels. 50 - ([source](https://github.com/vic/den/blob/main/modules/aspects/provides/user-shell.nix)) 51 52 ```nix 53 den.aspects.vic.includes = [ (den._.user-shell "fish") ]; ··· 63 ## unfree 64 65 Enables unfree packages by name. 66 - ([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)) 67 68 ```nix 69 den.aspects.laptop.includes = [ (den._.unfree [ "discord" ]) ]; ··· 82 ## tty-autologin 83 84 Automatic tty login. 85 86 ```nix 87 den.aspects.laptop.includes = [ (den._.tty-autologin "root") ]; ··· 94 ## import-tree 95 96 Auto-imports non-dendritic Nix files by class directory. 97 - ([source](https://github.com/vic/den/blob/main/modules/aspects/provides/import-tree.nix)) 98 99 ```nix 100 den.aspects.laptop.includes = [ (den._.import-tree ./path) ]; ··· 115 ## inputs' (flake-parts) 116 117 Provides per-system `inputs'` as a module argument. 118 119 ```nix 120 den.default.includes = [ den._.inputs' ]; ··· 125 ## self' (flake-parts) 126 127 Provides per-system `self'` as a module argument. 128 129 ```nix 130 den.default.includes = [ den._.self' ]; ··· 135 ## forward 136 137 Creates custom Nix classes by forwarding configs between classes. 138 - ([source](https://github.com/vic/den/blob/main/modules/aspects/provides/forward.nix) · [usage guide](/guides/custom-classes/)) 139 140 ```nix 141 den._.forward { ··· 154 HM integration is handled by 155 `den.ctx.hm-host` and `den.ctx.hm-user` context types, which are 156 activated automatically when hosts have HM users. 157 158 All `homeManager` class settings are forwarded to os-level `home-manager.users.<userName>`. 159 160 ## user 161 162 The `user` class is automatically handled by Den and forwards to os-level `users.users.<userName>` 163 164 You can write: 165
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 + <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> 9 10 ## Overview 11 ··· 15 ## define-user 16 17 Defines a user at OS and Home-Manager levels. 18 + ([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)) 19 20 ```nix 21 den.default.includes = [ den._.define-user ]; ··· 31 ## primary-user 32 33 Makes a user an administrator. 34 + ([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)) 35 36 ```nix 37 den.aspects.vic.includes = [ den._.primary-user ]; ··· 47 ## user-shell 48 49 Sets default shell at OS and HM levels. 50 + ([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)) 51 52 ```nix 53 den.aspects.vic.includes = [ (den._.user-shell "fish") ]; ··· 63 ## unfree 64 65 Enables unfree packages by name. 66 + ([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)) 67 68 ```nix 69 den.aspects.laptop.includes = [ (den._.unfree [ "discord" ]) ]; ··· 82 ## tty-autologin 83 84 Automatic tty login. 85 + ([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)) 86 87 ```nix 88 den.aspects.laptop.includes = [ (den._.tty-autologin "root") ]; ··· 95 ## import-tree 96 97 Auto-imports non-dendritic Nix files by class directory. 98 + ([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)) 99 100 ```nix 101 den.aspects.laptop.includes = [ (den._.import-tree ./path) ]; ··· 116 ## inputs' (flake-parts) 117 118 Provides per-system `inputs'` as a module argument. 119 + ([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)) 120 121 ```nix 122 den.default.includes = [ den._.inputs' ]; ··· 127 ## self' (flake-parts) 128 129 Provides per-system `self'` as a module argument. 130 + ([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)) 131 132 ```nix 133 den.default.includes = [ den._.self' ]; ··· 138 ## forward 139 140 Creates custom Nix classes by forwarding configs between classes. 141 + ([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/)) 142 143 ```nix 144 den._.forward { ··· 157 HM integration is handled by 158 `den.ctx.hm-host` and `den.ctx.hm-user` context types, which are 159 activated automatically when hosts have HM users. 160 + ([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/)) 161 162 All `homeManager` class settings are forwarded to os-level `home-manager.users.<userName>`. 163 164 ## user 165 166 The `user` class is automatically handled by Den and forwards to os-level `users.users.<userName>` 167 + ([source](https://github.com/vic/den/blob/main/modules/aspects/provides/os-user.nix)) 168 169 You can write: 170
-180
docs/src/content/docs/reference/ctx.md
··· 1 - --- 2 - title: den.ctx Reference 3 - description: Context type definitions and their built-in implementations. 4 - --- 5 - 6 - import { Aside } from '@astrojs/starlight/components'; 7 - 8 - <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> 9 - 10 - ## Context Type Schema 11 - 12 - Each `den.ctx.<name>` is a submodule with these options: 13 - 14 - | Option | Type | Description | 15 - |--------|------|-------------| 16 - | `desc` | `str` | Human-readable description | 17 - | `conf` | `ctx → aspect` | Locates the aspect for this context | 18 - | `into` | `attrset of (ctx → list)` | Transformations to other contexts | 19 - | `includes` | `list of aspect` | Parametric aspects activated for this context | 20 - 21 - Context types are also functors — callable as functions: 22 - 23 - ```nix 24 - aspect = den.ctx.host { host = den.hosts.x86_64-linux.igloo; }; 25 - ``` 26 - 27 - ## ctxApply (internal) 28 - 29 - When a context type is applied, `ctxApply` executes: 30 - 31 - ```nix 32 - ctxApply = self: ctx: { 33 - includes = lib.flatten [ 34 - (parametric.fixedTo ctx (cleanCtx self)) # owned configs 35 - (self.conf ctx) # located aspect 36 - (mapAttrsToList (name: into: # transformations 37 - map den.ctx.${name} (into ctx) 38 - ) self.into) 39 - ]; 40 - }; 41 - ``` 42 - 43 - ## Transformation Types 44 - 45 - All `into` transformations have the type `source → [ target ]`: 46 - 47 - - **Fan-out**: `{ host }: map (u: { inherit host user; }) users` — one host produces N contexts 48 - - **Conditional**: `{ host }: lib.optional (test host) { inherit host; }` — zero or one 49 - - **Pass-through**: `lib.singleton` — forward the same data as-is 50 - 51 - An empty list means the target context is not created. This enables 52 - conditional activation like HM detection without any explicit `if` logic 53 - in the pipeline. 54 - 55 - ## Contexts as Cutting-Points 56 - 57 - Context types have their own owned configs and `includes`, making them 58 - aspect-like cutting-points in the pipeline: 59 - 60 - ```nix 61 - den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true; 62 - den.ctx.hm-host.includes = [ 63 - ({ host, ... }: { nixos.home-manager.backupFileExtension = "bak"; }) 64 - ]; 65 - ``` 66 - 67 - These only activate for validated HM hosts — more precise than 68 - `den.default.includes` which runs at every pipeline stage. 69 - 70 - ## Extending Context Flow 71 - 72 - Add transformations to existing context types from any module: 73 - 74 - ```nix 75 - den.ctx.hm-host.into.foo = { host }: [ { foo = host.name; } ]; 76 - den.ctx.foo.conf = { foo }: { funny.names = [ foo ]; }; 77 - ``` 78 - 79 - The module system merges these definitions. You can also override a 80 - host's `mainModule` to use a completely custom context flow. 81 - 82 - ## Built-in Context Types 83 - 84 - ### den.ctx.host 85 - 86 - | Field | Value | 87 - |-------|-------| 88 - | `desc` | OS | 89 - | `conf` | `{ host }:` fixedTo host aspect | 90 - | `into.default` | `lib.singleton` (pass-through) | 91 - | `into.user` | Enumerate `host.users` | 92 - | `into.hm-host` | Detect HM support (from `hm-detect.nix`) | 93 - 94 - ### den.ctx.user 95 - 96 - | Field | Value | 97 - |-------|-------| 98 - | `desc` | OS user | 99 - | `conf` | `{ host, user }:` includes user + host aspects | 100 - | `into.default` | `lib.singleton` (pass-through) | 101 - 102 - ### den.ctx.default 103 - 104 - | Field | Value | 105 - |-------|-------| 106 - | `conf` | `_: { }` (empty — just a dispatcher) | 107 - 108 - Aliased as `den.default` via `lib.mkAliasOptionModule`. 109 - Writing `den.default.foo` is identical to `den.ctx.default.foo`. 110 - 111 - Every context type transforms into `default`, so `den.default.includes` 112 - functions run at every pipeline stage. Use `take.exactly` to restrict 113 - matching if you see duplicate values. 114 - 115 - ### den.ctx.home 116 - 117 - | Field | Value | 118 - |-------|-------| 119 - | `desc` | Standalone Home-Manager config | 120 - | `conf` | `{ home }:` fixedTo home aspect | 121 - | `into.default` | `lib.singleton` | 122 - 123 - ### den.ctx.hm-host 124 - 125 - | Field | Value | 126 - |-------|-------| 127 - | `desc` | Host with HM-supported OS and HM users | 128 - | `conf` | `{ host }:` imports HM NixOS/Darwin module | 129 - | `into.hm-user` | Enumerate HM-class users | 130 - 131 - **Detection criteria** (all must be true): 132 - 1. Host class is `nixos` or `darwin` 133 - 2. At least one user has `class = "homeManager"` 134 - 3. `inputs.home-manager` exists, or host has a custom `hm-module` attribute 135 - 136 - If detection fails, the HM pipeline is skipped entirely. 137 - 138 - ### den.ctx.hm-user 139 - 140 - | Field | Value | 141 - |-------|-------| 142 - | `desc` | Internal — forwards HM class to host | 143 - | `conf` | `{ host, user }:` forward homeManager into host | 144 - 145 - ### den.ctx.hm-internal-user (internal) 146 - 147 - An internal context type used by `hm-user`. It combines: 148 - - The user context (`den.ctx.user { host, user }`) 149 - - Owned configs from the host aspect 150 - - Static includes from the host aspect 151 - - Parametric includes from the host aspect matching `{ host, user }` 152 - 153 - This ensures that when the `homeManager` class is forwarded into 154 - `home-manager.users.<name>`, it receives contributions from both 155 - the user's aspect and the host's aspect. 156 - 157 - ## Defining Custom Context Types 158 - 159 - Context types are independent of NixOS. Use them for any domain: 160 - 161 - ```nix 162 - den.ctx.myctx = { 163 - desc = "{x, y} context"; 164 - conf = { x, y }: { funny.names = [ x y ]; }; 165 - includes = [ 166 - ({ x, ... }: { funny.names = [ "include-${x}" ]; }) 167 - ]; 168 - into = { 169 - other = { x, y }: [{ z = x + y; }]; 170 - }; 171 - }; 172 - ``` 173 - 174 - Owned attributes (anything not `desc`, `conf`, `into`, `includes`) 175 - are included when the context is applied. 176 - 177 - Custom context flows can be designed and tested in isolation — Den's 178 - CI tests use a `funny.names` class that has nothing to do with NixOS 179 - to verify the context mechanics independently.See the [CI test suite](https://github.com/vic/den/tree/main/templates/ci/modules) 180 - for examples.
···
+169
docs/src/content/docs/reference/ctx.mdx
···
··· 1 + --- 2 + title: den.ctx Reference 3 + description: Context type definitions and their built-in implementations. 4 + --- 5 + 6 + import { Aside } from '@astrojs/starlight/components'; 7 + 8 + <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> 9 + 10 + ## Context Type Schema 11 + 12 + Each `den.ctx.<name>` is an [aspect submodule](/explanation/aspects/) extended 13 + with context transformations. It inherits all options from `flake-aspects`'s 14 + `aspect`-type and adds `into`, and a couple of conventions: 15 + 16 + | Option | Type | Description | 17 + |--------|------|-------------| 18 + | `description` | `str` | Human-readable description | 19 + | `provides.${name}` | `ctx → aspect` | Self-named provider: locates the aspect for this context | 20 + | `provides.${target}` | `ctx → aspect` | Cross-provider: source's contribution to target contexts | 21 + | `into` | `attrset of (ctx → list)` | Transformations to other contexts | 22 + | `includes` | `list of aspect` | Parametric aspects activated for this context | 23 + 24 + Context types are also functors — callable as functions: 25 + 26 + ```nix 27 + aspect = den.ctx.host { host = den.hosts.x86_64-linux.igloo; }; 28 + ``` 29 + 30 + ## ctxApply (internal) 31 + 32 + When a context type is applied, `ctxApply` walks the full `into` graph 33 + and deduplicates includes using a seen-set: 34 + 35 + ```nix 36 + ctxApply = ctxName: _self: ctx: 37 + let 38 + pairs = collectPairs null den.ctx.${ctxName} ctx; 39 + in 40 + { includes = dedupIncludes pairs; }; 41 + ``` 42 + 43 + **collectPairs** recursively walks `into` transformations, producing 44 + `(source, ctxDef, ctx)` triples for every reachable context type. 45 + The `source` tracks which context type produced the transformation. 46 + 47 + **dedupIncludes** processes triples with a seen-set keyed by context name: 48 + 49 + - **First visit**: `fixedTo ctx (cleanCtx ctxDef)` — dispatches owned, static, and parametric includes — plus self-provider and cross-provider 50 + - **Subsequent visits**: `atLeast (cleanCtx ctxDef) ctx` — dispatches only parametric includes — plus self-provider and cross-provider 51 + 52 + For each triple, three providers are called: 53 + 1. **Self-provider** `ctxDef.provides.${ctxDef.name}` — the target's own aspect lookup 54 + 2. **Cross-provider** `source.provides.${ctxDef.name}` — the source's contribution to this target (if defined) 55 + 56 + ## Transformation Types 57 + 58 + All `into` transformations have the type `source → [ target ]`: 59 + 60 + - **Fan-out**: `{ host }: map (u: { inherit host user; }) users` — one host produces N contexts 61 + - **Conditional**: `{ host }: lib.optional (test host) { inherit host; }` — zero or one 62 + - **Pass-through**: `lib.singleton` — forward the same data as-is 63 + 64 + An empty list means the target context is not created. This enables 65 + conditional activation like HM detection without any explicit `if` logic 66 + in the pipeline. 67 + 68 + ## Contexts as Cutting-Points 69 + 70 + Context types have their own owned configs and `includes`, making them 71 + aspect-like cutting-points in the pipeline: 72 + 73 + ```nix 74 + den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true; 75 + den.ctx.hm-host.includes = [ 76 + ({ host, ... }: { nixos.home-manager.backupFileExtension = "bak"; }) 77 + ]; 78 + ``` 79 + 80 + 81 + ## Extending Context Flow 82 + 83 + Add transformations to existing context types from any module: 84 + 85 + ```nix 86 + den.ctx.hm-host.into.foo = { host }: [ { foo = host.name; } ]; 87 + den.ctx.foo._.foo = { foo }: { funny.names = [ foo ]; }; 88 + ``` 89 + 90 + The module system merges these definitions. You can also override a 91 + host's `mainModule` to use a completely custom context flow. 92 + 93 + ## Built-in Context Types 94 + 95 + ### den.ctx.host 96 + 97 + | Field | Value | 98 + |-------|-------| 99 + | `description` | OS | 100 + | `provides.host` | `{ host }:` fixedTo host aspect | 101 + | `provides.user` | `{ host, user }:` host's contribution to user contexts (cross-provider) | 102 + | `into.default` | `lib.singleton` (pass-through) | 103 + | `into.user` | Enumerate `host.users` | 104 + | `into.hm-host` | Detect HM support (from `hm-detect.nix`) | 105 + 106 + ### den.ctx.user 107 + 108 + | Field | Value | 109 + |-------|-------| 110 + | `description` | OS user | 111 + | `provides.user` | `{ host, user }:` fixedTo user aspect | 112 + | `into.default` | `lib.singleton` (pass-through) | 113 + 114 + ### den.ctx.default 115 + 116 + | Field | Value | 117 + |-------|-------| 118 + | `provides.default` | `_: { }` (empty — just a dispatcher) | 119 + 120 + Aliased as `den.default` via `lib.mkAliasOptionModule`. 121 + Writing `den.default.foo` is identical to `den.ctx.default.foo`. 122 + 123 + Host/user/home context types transforms into `default`, so `den.default.includes` 124 + functions run at each of their pipeline stages. However, owned and static configs 125 + are **automatically deduplicated** — only the first visit gets them. 126 + Parametric functions still run at every stage; use `take.exactly` to 127 + restrict matching if needed. 128 + 129 + ### den.ctx.home 130 + 131 + | Field | Value | 132 + |-------|-------| 133 + | `description` | Standalone Home-Manager config | 134 + | `provides.home` | `{ home }:` fixedTo home aspect | 135 + | `into.default` | `lib.singleton` | 136 + 137 + ### den.ctx.hm-host 138 + 139 + | Field | Value | 140 + |-------|-------| 141 + | `description` | Host with HM-supported OS and HM users | 142 + | `provides.hm-host` | `{ host }:` imports HM NixOS/Darwin module | 143 + | `into.hm-user` | Enumerate HM-class users | 144 + 145 + **Detection criteria** (all must be true): 146 + 1. Host class is `nixos` or `darwin` 147 + 2. At least one user has `class = "homeManager"` 148 + 3. `inputs.home-manager` exists, or host has a custom `hm-module` attribute 149 + 150 + If detection fails, the HM pipeline is skipped entirely. 151 + 152 + ### den.ctx.hm-user 153 + 154 + | Field | Value | 155 + |-------|-------| 156 + | `description` | Internal — forwards HM class to host | 157 + | `provides.hm-user` | `{ host, user }:` forward homeManager into host | 158 + 159 + ### den.ctx.hm-internal-user (internal) 160 + 161 + An internal context type used by `hm-user`. It combines: 162 + - The user context (`den.ctx.user { host, user }`) 163 + - Owned configs from the host aspect 164 + - Static includes from the host aspect 165 + - Parametric includes from the host aspect matching `{ host, user }` 166 + 167 + This ensures that when the `homeManager` class is forwarded into 168 + `home-manager.users.<name>`, it receives contributions from both 169 + the user's aspect and the host's aspect.
+24 -5
docs/src/content/docs/reference/lib.md docs/src/content/docs/reference/lib.mdx
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <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> 9 10 ## den.lib.parametric 11 12 - Creates a parametric aspect. Alias for `parametric.withOwn parametric.atLeast`. 13 14 ```nix 15 den.lib.parametric { nixos.x = 1; includes = [ f ]; } ··· 21 22 Dispatches **only** to functions whose required args are a subset of context: 23 24 ```nix 25 F = parametric.atLeast { includes = [ a b ]; }; 26 ``` ··· 28 ### parametric.exactly 29 30 Dispatches **only** to functions whose args match context exactly: 31 32 ```nix 33 F = parametric.exactly { includes = [ a b ]; }; ··· 37 38 Replaces context with a fixed attribute set: 39 40 ```nix 41 F = parametric.fixedTo { x = 1; } aspect; 42 ``` ··· 44 ### parametric.expands 45 46 Merges extra attributes into received context: 47 48 ```nix 49 F = parametric.expands { extra = 1; } aspect; ··· 52 ### parametric.withOwn 53 54 Combinator: adds owned + statics on top of a dispatch functor: 55 56 ```nix 57 F = parametric.withOwn parametric.exactly aspect; 58 ``` 59 ··· 69 70 Wraps function to only be called when context has at least the required args. 71 72 ### take.exactly 73 74 ```nix ··· 76 ``` 77 78 Only called when context has exactly these args, no more. 79 80 ### take.unused 81 ··· 89 90 Function signature introspection: 91 92 ```nix 93 den.lib.canTake { x = 1; } someFunction 94 # => true if someFunction can take at least { x } ··· 99 100 ## den.lib.aspects 101 102 - Re-export of `flake-aspects` library. Provides: 103 - `aspects.types.aspectsType` — module type for aspect trees 104 - `aspects.types.providerType` — type for aspect providers 105 - `aspects.forward` — class forwarding implementation ··· 114 115 ## den.lib.owned 116 117 - Extracts only owned configs from an aspect (removes `includes`, `__functor`): 118 119 ```nix 120 den.lib.owned someAspect # => { nixos = ...; darwin = ...; } ··· 124 125 Creates a functor that only resolves static includes from an aspect: 126 127 ```nix 128 - den.lib.statics someAspect { class = "nixos"; aspect-chain = []; } 129 ``` 130 131 ## den.lib.isStatic
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 + <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> 9 10 ## den.lib.parametric 11 12 + Creates a parametric aspect. Alias for `(parametric.withOwn parametric.atLeast)`. 13 14 ```nix 15 den.lib.parametric { nixos.x = 1; includes = [ f ]; } ··· 21 22 Dispatches **only** to functions whose required args are a subset of context: 23 24 + **does include owned config, atLeast only forwards context**. 25 + 26 ```nix 27 F = parametric.atLeast { includes = [ a b ]; }; 28 ``` ··· 30 ### parametric.exactly 31 32 Dispatches **only** to functions whose args match context exactly: 33 + 34 + **does include owned config, atLeast only forwards context**. 35 36 ```nix 37 F = parametric.exactly { includes = [ a b ]; }; ··· 41 42 Replaces context with a fixed attribute set: 43 44 + Ignores any given context at call site, and replaces with a fixed one. 45 + Behaves like `parametric`, includes owned + static includes + function includes. 46 + 47 ```nix 48 F = parametric.fixedTo { x = 1; } aspect; 49 ``` ··· 51 ### parametric.expands 52 53 Merges extra attributes into received context: 54 + Behaves like `parametric`, includes owned + static includes + function includes. 55 56 ```nix 57 F = parametric.expands { extra = 1; } aspect; ··· 60 ### parametric.withOwn 61 62 Combinator: adds owned + statics on top of a dispatch functor: 63 + This is how `parametric` function itself uses `parametric.atLeast`. 64 65 ```nix 66 + # an exactly variant of parametric. 67 F = parametric.withOwn parametric.exactly aspect; 68 ``` 69 ··· 79 80 Wraps function to only be called when context has at least the required args. 81 82 + Produces an empty attrset if the context has not atLeast the expected args. 83 + 84 ### take.exactly 85 86 ```nix ··· 88 ``` 89 90 Only called when context has exactly these args, no more. 91 + 92 + Produces an empty attrset otherwise. 93 94 ### take.unused 95 ··· 103 104 Function signature introspection: 105 106 + Does not care about values, only about attribute names. 107 + 108 ```nix 109 den.lib.canTake { x = 1; } someFunction 110 # => true if someFunction can take at least { x } ··· 115 116 ## den.lib.aspects 117 118 + Re-export of [`flake-aspects`](https://github.com/vic/flake-aspects) library. Provides: 119 - `aspects.types.aspectsType` — module type for aspect trees 120 - `aspects.types.providerType` — type for aspect providers 121 - `aspects.forward` — class forwarding implementation ··· 130 131 ## den.lib.owned 132 133 + Extracts only owned configs from an aspect (**removes** `includes`, `__functor` from someAspect): 134 135 ```nix 136 den.lib.owned someAspect # => { nixos = ...; darwin = ...; } ··· 140 141 Creates a functor that only resolves static includes from an aspect: 142 143 + The new aspect ignores owned configs, and functional includes that are not 144 + of type (ground flake-aspects): `{ class, aspect-chain }: {...}` 145 + 146 ```nix 147 + den.lib.statics someAspect # => <aspect with only static includes> 148 ``` 149 150 ## den.lib.isStatic
+1 -1
docs/src/content/docs/reference/output.md docs/src/content/docs/reference/output.mdx
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <Aside type="tip">Source: [`modules/config.nix`](https://github.com/vic/den/blob/main/modules/config.nix) · [`modules/output.nix`](https://github.com/vic/den/blob/main/modules/output.nix)</Aside> 9 10 ## Overview 11
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 + <Aside title="Use the Source, Luke" icon="github">[`modules/config.nix`](https://github.com/vic/den/blob/main/modules/config.nix) · [`modules/output.nix`](https://github.com/vic/den/blob/main/modules/output.nix) · Tests: [`default-includes.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/default-includes.nix) · [`home-manager/`](https://github.com/vic/den/tree/main/templates/ci/modules/features/home-manager)</Aside> 9 10 ## Overview 11
+4 -19
docs/src/content/docs/reference/schema.md docs/src/content/docs/reference/schema.mdx
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 - <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> 9 10 ## den.hosts 11 ··· 92 ``` 93 94 `den.base.conf` is automatically imported by host, user, and home. 95 - 96 - ## den.ful 97 - 98 - Namespace storage for aspect libraries: 99 - 100 - ```nix 101 - den.ful.<namespace>.<name> = aspect; 102 - ``` 103 - 104 - Populated via [`inputs.den.namespace`](/guides/namespaces/). 105 - 106 - ## flake.denful 107 - 108 - Flake output for sharing namespaces: 109 - 110 - ```nix 111 - flake.denful.<namespace> = den.ful.<namespace>; 112 - ```
··· 5 6 import { Aside } from '@astrojs/starlight/components'; 7 8 + 9 + <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> 10 + 11 + `den.base` provides extensible schemas for `host`/`user`/`home`. 12 13 ## den.hosts 14 ··· 95 ``` 96 97 `den.base.conf` is automatically imported by host, user, and home.
+109
docs/src/content/docs/tutorials/bogus.md
···
··· 1 + --- 2 + title: "Template: Bug Reproduction" 3 + description: Create minimal reproductions for Den bug reports using nix-unit. 4 + --- 5 + 6 + The bogus template helps you create minimal bug reproductions. Use it when reporting issues or contributing fixes. 7 + 8 + ## Initialize 9 + 10 + ```console 11 + mkdir bogus && cd bogus 12 + nix flake init -t github:vic/den#bogus 13 + nix flake update den 14 + ``` 15 + 16 + ## Project Structure 17 + 18 + ``` 19 + flake.nix 20 + modules/ 21 + bug.nix # your bug reproduction 22 + test-base.nix # test infrastructure (DO NOT EDIT) 23 + ``` 24 + 25 + ## Writing a Bug Reproduction 26 + 27 + Edit `modules/bug.nix` with a minimal test case: 28 + 29 + ```nix 30 + { denTest, ... }: 31 + { 32 + flake.tests.bogus = { 33 + test-something = denTest ( 34 + { den, lib, igloo, tuxHm, ... }: 35 + { 36 + den.hosts.x86_64-linux.igloo.users.tux = { }; 37 + 38 + # set up the scenario 39 + den.aspects.igloo.nixos.something = true; 40 + 41 + # what you get 42 + expr = igloo.something; 43 + # what you expect 44 + expected = true; 45 + } 46 + ); 47 + }; 48 + } 49 + ``` 50 + 51 + ### How denTest Works 52 + 53 + `denTest` is a helper that: 54 + 1. Creates a fresh Den evaluation with your module 55 + 2. Provides helpers like `igloo` (host config), `tuxHm` (user's HM config) 56 + 3. Compares `expr` against `expected` using [nix-unit](https://github.com/nix-community/nix-unit) 57 + 58 + Available test helpers (from `test-base.nix`): 59 + 60 + | Helper | Description | 61 + |--------|-------------| 62 + | `igloo` | `nixosConfigurations.igloo.config` | 63 + | `iceberg` | `nixosConfigurations.iceberg.config` | 64 + | `tuxHm` | `igloo.home-manager.users.tux` | 65 + | `pinguHm` | `igloo.home-manager.users.pingu` | 66 + | `funnyNames` | Resolves an aspect for class `"funny"` and collects `.names` | 67 + | `show` | `builtins.trace` helper for debugging | 68 + 69 + ## Run Tests 70 + 71 + ```console 72 + nix flake check 73 + ``` 74 + 75 + ## Testing Against Different Den Versions 76 + 77 + Edit `.github/workflows/test.yml` to test against multiple Den versions: 78 + 79 + ```yaml 80 + strategy: 81 + matrix: 82 + rev: ["main", "v1.0.0", "abc1234"] 83 + ``` 84 + 85 + This helps identify regressions — include `"main"` and any release tag or commit. 86 + 87 + ## Contributing a Fix 88 + 89 + If you're submitting a fix to Den, test against your local checkout: 90 + 91 + ```console 92 + cd <den-working-copy> 93 + nix flake check --override-input den . ./templates/bogus 94 + ``` 95 + 96 + ## What It Provides 97 + 98 + | Feature | Provided | 99 + |---------|:--------:| 100 + | nix-unit test infrastructure | ✓ | 101 + | Pre-configured denTest helper | ✓ | 102 + | Version matrix testing | ✓ | 103 + | Common test helpers | ✓ | 104 + 105 + ## Next Steps 106 + 107 + - Share your reproduction repo on [GitHub Discussions](https://github.com/vic/den/discussions) 108 + - Read [Debug Configurations](/guides/debug/) for debugging techniques 109 + - See the [CI Tests template](/tutorials/ci/) for Den's own comprehensive test suite
+164
docs/src/content/docs/tutorials/ci.md
···
··· 1 + --- 2 + title: "Template: CI Tests" 3 + description: Den's own test suite — the definitive reference for every feature. 4 + --- 5 + 6 + 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. 7 + 8 + ## Structure 9 + 10 + ``` 11 + flake.nix 12 + modules/ 13 + empty.nix # example test skeleton 14 + test-support/ 15 + eval-den.nix # denTest + evalDen helpers 16 + nix-unit.nix # nix-unit integration 17 + features/ 18 + angle-brackets.nix # <den/...> syntax 19 + conditional-config.nix # conditional imports/configs 20 + default-includes.nix # den.default behavior 21 + forward.nix # den._.forward 22 + homes.nix # standalone HM 23 + host-options.nix # host/user schema options 24 + namespaces.nix # namespace define/merge/export 25 + os-user-class.nix # user class forwarding 26 + parametric.nix # parametric functors 27 + schema-base-modules.nix # den.base modules 28 + special-args-custom-instantiate.nix # custom instantiation 29 + top-level-parametric.nix # top-level context aspects 30 + user-host-bidirectional-config.nix # bidirectional providers 31 + batteries/ 32 + define-user.nix # define-user battery 33 + flake-parts.nix # inputs' and self' 34 + import-tree.nix # import-tree battery 35 + primary-user.nix # primary-user battery 36 + tty-autologin.nix # tty-autologin battery 37 + unfree.nix # unfree packages 38 + user-shell.nix # user-shell battery 39 + context/ 40 + apply.nix # ctx application 41 + apply-non-exact.nix # non-exact matching 42 + cross-provider.nix # cross-provider mechanism 43 + custom-ctx.nix # custom context types 44 + den-default.nix # den.default as context 45 + host-propagation.nix # full host pipeline 46 + named-provider.nix # self-named providers 47 + deadbugs/ 48 + _external-namespace-deep-aspect.nix 49 + static-include-dup-package.nix 50 + home-manager/ 51 + home-managed-home.nix 52 + use-global-pkgs.nix 53 + non-dendritic/ # non-den files for import-tree tests 54 + provider/ # external namespace provider flake 55 + ``` 56 + 57 + ## Test Categories 58 + 59 + ### Core Features 60 + 61 + | Test File | What It Tests | 62 + |-----------|---------------| 63 + | [conditional-config.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/conditional-config.nix) | Conditional imports using host/user attributes | 64 + | [default-includes.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/default-includes.nix) | `den.default` applying to all hosts/users | 65 + | [host-options.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/host-options.nix) | Custom host attributes, hostName, aspect names | 66 + | [top-level-parametric.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/top-level-parametric.nix) | Context-aware top-level aspects | 67 + | [parametric.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/parametric.nix) | All parametric functors (atLeast, fixedTo, expands) | 68 + 69 + ### Bidirectional & Providers 70 + 71 + | Test File | What It Tests | 72 + |-----------|---------------| 73 + | [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 | 74 + | [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 | 75 + | [context/named-provider.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/named-provider.nix) | Self-named provider mechanism | 76 + 77 + ### Context System 78 + 79 + | Test File | What It Tests | 80 + |-----------|---------------| 81 + | [context/apply.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/context/apply.nix) | Context application mechanics | 82 + | [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 | 83 + | [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` | 84 + | [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 | 85 + | [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 | 86 + 87 + ### Batteries 88 + 89 + | Test File | What It Tests | 90 + |-----------|---------------| 91 + | [batteries/define-user.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/define-user.nix) | User definition across contexts | 92 + | [batteries/primary-user.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/primary-user.nix) | Primary user groups | 93 + | [batteries/user-shell.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/user-shell.nix) | Shell configuration | 94 + | [batteries/unfree.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/unfree.nix) | Unfree package predicates | 95 + | [batteries/tty-autologin.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/tty-autologin.nix) | TTY autologin service | 96 + | [batteries/import-tree.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/import-tree.nix) | Auto-importing class dirs | 97 + | [batteries/flake-parts.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/batteries/flake-parts.nix) | `inputs'` and `self'` providers | 98 + 99 + ### Advanced 100 + 101 + | Test File | What It Tests | 102 + |-----------|---------------| 103 + | [angle-brackets.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/angle-brackets.nix) | All bracket resolution paths | 104 + | [namespaces.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/namespaces.nix) | Local, remote, merged namespaces | 105 + | [forward.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/forward.nix) | Custom class forwarding | 106 + | [homes.nix](https://github.com/vic/den/blob/main/templates/ci/modules/features/homes.nix) | Standalone Home-Manager configs | 107 + | [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}` | 108 + 109 + ### Bug Regressions 110 + 111 + | Test File | What It Tests | 112 + |-----------|---------------| 113 + | [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 | 114 + | [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 | 115 + 116 + ### External Provider 117 + 118 + 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: 119 + 120 + ```nix 121 + # provider/modules/den.nix 122 + { inputs, ... }: 123 + { 124 + imports = [ inputs.den.flakeModule (inputs.den.namespace "provider" true) ]; 125 + provider.tools._.dev._.editors = { 126 + nixos.programs.vim.enable = true; 127 + }; 128 + } 129 + ``` 130 + 131 + ## Running CI Tests 132 + 133 + From the Den root against your local checkout: 134 + 135 + ```console 136 + nix flake check --override-input den . ./templates/ci 137 + ``` 138 + 139 + You can also run a single or a subset of tests using: 140 + 141 + ```console 142 + # You can use any attr-path bellow flake.tests after system-agnositc to run those specific tests: 143 + nix-unit --override-input den . --flake ./templates/ci#.tests.systems.x86_64-linux.system-agnostic 144 + ``` 145 + 146 + ## Writing New Tests 147 + 148 + Copy `modules/empty.nix` as a starting point: 149 + 150 + ```nix 151 + { denTest, ... }: 152 + { 153 + flake.tests.my-feature = { 154 + test-name = denTest ( 155 + { den, igloo, ... }: 156 + { 157 + den.hosts.x86_64-linux.igloo.users.tux = { }; 158 + expr = /* what you get */; 159 + expected = /* what you expect */; 160 + } 161 + ); 162 + }; 163 + } 164 + ```
-115
docs/src/content/docs/tutorials/context-aware.md
··· 1 - --- 2 - title: Context-Aware Configurations 3 - description: Make aspects produce conditional config based on host and user context. 4 - --- 5 - 6 - ## From Static to Dynamic 7 - 8 - So far, aspects have been static attribute sets. But Den's power comes from 9 - making them **functions of context**. When an aspect is a function, Den 10 - passes it the current context — host, user, platform — and the function 11 - decides what to produce. 12 - 13 - ## Step 1: A Simple Context Function 14 - 15 - Instead of a static aspect, write a function: 16 - 17 - ```nix 18 - { den, ... }: { 19 - den.aspects.igloo.includes = [ 20 - ({ host, ... }: { 21 - nixos.networking.hostName = host.name; 22 - }) 23 - ]; 24 - } 25 - ``` 26 - 27 - Den calls this function with `{ host }` containing the host's attributes. 28 - The result sets the hostname dynamically from the host's name. 29 - 30 - ## Step 2: React to User Context 31 - 32 - Functions receiving `{ host, user, ... }` run once per user on each host: 33 - 34 - ```nix 35 - { den, ... }: { 36 - den.default.includes = [ 37 - ({ host, user, ... }: { 38 - ${host.class}.users.users.${user.userName}.description = 39 - "${user.userName} on ${host.name}"; 40 - }) 41 - ]; 42 - } 43 - ``` 44 - 45 - Notice `${host.class}` — this dynamically targets `nixos` or `darwin` 46 - depending on the host's platform. The same function works on both. 47 - 48 - ## Step 3: Conditional Configuration 49 - 50 - Use standard Nix conditionals inside context functions: 51 - 52 - ```nix 53 - { den, lib, ... }: 54 - let 55 - git-for-linux = { user, host, ... }: 56 - if !lib.hasSuffix "darwin" host.system 57 - then { homeManager.programs.git.enable = true; } 58 - else { }; 59 - in { 60 - den.aspects.tux.includes = [ git-for-linux ]; 61 - } 62 - ``` 63 - 64 - Git is enabled only for users on Linux hosts. On Darwin, the function 65 - returns an empty set — no effect. 66 - 67 - ## Step 4: Use den.lib.parametric 68 - 69 - For aspects that need to forward context to their own includes, 70 - wrap them with `den.lib.parametric`: 71 - 72 - ```nix 73 - { den, ... }: 74 - let 75 - workspace = den.lib.parametric { 76 - nixos.networking.hostName = "from-parametric"; 77 - includes = [ 78 - ({ host, ... }: { nixos.time.timeZone = "UTC"; }) 79 - ]; 80 - }; 81 - in { 82 - den.aspects.igloo.includes = [ workspace ]; 83 - } 84 - ``` 85 - 86 - The `parametric` functor ensures that: 87 - 1. **Owned** config (`nixos.networking.hostName`) is always included 88 - 2. **Functions** in `includes` receive the forwarded context 89 - 3. Functions whose parameters don't match are **silently skipped** 90 - 91 - ## Step 5: Understand Matching 92 - 93 - Den matches context by **argument names**, not values: 94 - 95 - | Function signature | `{ host }` | `{ host, user }` | 96 - |--------------------|:---:|:---:| 97 - | `{ host, ... }: ...` | ✓ | ✓ | 98 - | `{ host, user }: ...` | ✗ | ✓ | 99 - | `{ never, ... }: ...` | ✗ | ✗ | 100 - 101 - - `atLeast` — function is called if context has **at least** the required args 102 - - `exactly` — function is called only if context has **exactly** those args 103 - 104 - ## What You've Learned 105 - 106 - - Aspects can be functions that receive `{ host, user, home, ... }` 107 - - Context functions produce conditional, platform-aware configurations 108 - - [`den.lib.parametric`](/explanation/parametric/) forwards context to nested includes 109 - - Unmatched functions are [silently skipped](/explanation/parametric/#matching-rules-summary) — no errors 110 - - `${host.class}` makes configs target the right Nix class dynamically 111 - 112 - ## Next 113 - 114 - - [Bidirectional Dependencies](/guides/bidirectional/) — hosts and users configure each other 115 - - [Context System](/explanation/context-system/) — deep dive into `den.ctx`
···
+177
docs/src/content/docs/tutorials/default.md
···
··· 1 + --- 2 + title: "Template: Default" 3 + description: Recommended starting point with flake-parts, Home-Manager, and VM testing. 4 + --- 5 + 6 + 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. 7 + 8 + ## Initialize 9 + 10 + ```console 11 + mkdir my-nix && cd my-nix 12 + nix flake init -t github:vic/den#default 13 + nix flake update den 14 + ``` 15 + 16 + ## Project Structure 17 + 18 + ``` 19 + flake.nix # auto-generated by flake-file 20 + modules/ 21 + dendritic.nix # flake-file + den dendritic setup 22 + hosts.nix # host and user declarations 23 + defaults.nix # global settings (stateVersion, etc.) 24 + igloo.nix # host aspect 25 + tux.nix # user aspect 26 + vm.nix # VM runner 27 + ``` 28 + 29 + ## File by File 30 + 31 + ### hosts.nix — Declare Your Infrastructure 32 + 33 + ```nix 34 + { 35 + den.hosts.x86_64-linux.igloo.users.tux = { }; 36 + } 37 + ``` 38 + 39 + This single line declares: 40 + - A host `igloo` on `x86_64-linux` (class: `nixos`) 41 + - A user `tux` on that host (class: `homeManager`) 42 + 43 + The host and user each get an aspect (`den.aspects.igloo` and `den.aspects.tux`) that you configure in separate files. 44 + 45 + ### igloo.nix — Host Aspect 46 + 47 + ```nix 48 + { 49 + den.aspects.igloo = { 50 + nixos = { pkgs, ... }: { 51 + environment.systemPackages = [ pkgs.hello ]; 52 + }; 53 + homeManager = { pkgs, ... }: { 54 + home.packages = [ pkgs.vim ]; 55 + }; 56 + }; 57 + } 58 + ``` 59 + 60 + The host aspect provides: 61 + - **NixOS config** — system packages available to all users 62 + - **Home-Manager config** — default home environment for every user on this host 63 + 64 + ### tux.nix — User Aspect 65 + 66 + ```nix 67 + { den, ... }: 68 + { 69 + den.aspects.tux = { 70 + includes = [ 71 + den.provides.primary-user 72 + (den.provides.user-shell "fish") 73 + ]; 74 + homeManager = { pkgs, ... }: { 75 + home.packages = [ pkgs.htop ]; 76 + }; 77 + }; 78 + } 79 + ``` 80 + 81 + The user aspect: 82 + - Includes [`primary-user`](/reference/batteries/#primary-user) — adds wheel + networkmanager groups 83 + - Includes [`user-shell`](/reference/batteries/#user-shell) — sets fish as default shell at OS and HM level 84 + - Provides personal Home-Manager packages 85 + 86 + ### defaults.nix — Global Settings 87 + 88 + ```nix 89 + { 90 + den.default.nixos.system.stateVersion = "25.11"; 91 + den.default.homeManager.home.stateVersion = "25.11"; 92 + } 93 + ``` 94 + 95 + `den.default` applies settings to **all** hosts, users, and homes. This is the right place for `stateVersion` and other global policies. 96 + 97 + ### vm.nix — Test in a VM 98 + 99 + ```nix 100 + { inputs, den, ... }: 101 + { 102 + den.aspects.igloo.includes = [ (den.provides.tty-autologin "tux") ]; 103 + 104 + perSystem = { pkgs, ... }: { 105 + packages.vm = pkgs.writeShellApplication { 106 + name = "vm"; 107 + text = let 108 + host = inputs.self.nixosConfigurations.igloo.config; 109 + in '' 110 + ${host.system.build.vm}/bin/run-${host.networking.hostName}-vm "$@" 111 + ''; 112 + }; 113 + }; 114 + } 115 + ``` 116 + 117 + Run the VM with: 118 + 119 + ```console 120 + nix run .#vm 121 + ``` 122 + 123 + ### dendritic.nix — Flake Wiring 124 + 125 + ```nix 126 + { inputs, ... }: 127 + { 128 + imports = [ 129 + (inputs.flake-file.flakeModules.dendritic or { }) 130 + (inputs.den.flakeModules.dendritic or { }) 131 + ]; 132 + flake-file.inputs = { 133 + den.url = "github:vic/den"; 134 + flake-file.url = "github:vic/flake-file"; 135 + home-manager = { 136 + url = "github:nix-community/home-manager"; 137 + inputs.nixpkgs.follows = "nixpkgs"; 138 + }; 139 + }; 140 + } 141 + ``` 142 + 143 + 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. 144 + 145 + ## Data Flow 146 + 147 + ```mermaid 148 + graph TD 149 + H["hosts.nix: igloo + tux"] --> CH["ctx.host {host=igloo}"] 150 + CH --> AI["den.aspects.igloo<br/>nixos + homeManager"] 151 + CH --> CU["ctx.user {host=igloo, user=tux}"] 152 + CU --> AT["den.aspects.tux<br/>primary-user + fish + htop"] 153 + CH --> HM["ctx.hm-host: import HM module"] 154 + HM --> HMU["ctx.hm-user: forward homeManager<br/>into home-manager.users.tux"] 155 + AI --> NixOS["nixosConfigurations.igloo"] 156 + AT --> NixOS 157 + HMU --> NixOS 158 + ``` 159 + 160 + ## What It Provides 161 + 162 + | Feature | Provided | 163 + |---------|:--------:| 164 + | NixOS host configuration | ✓ | 165 + | Home-Manager integration | ✓ | 166 + | Dendritic flake-file | ✓ | 167 + | VM testing | ✓ | 168 + | flake-parts | ✓ | 169 + | Darwin support | Add input | 170 + | Namespaces | Add manually | 171 + 172 + ## Next Steps 173 + 174 + - Edit `hosts.nix` to add more hosts or users 175 + - Create new aspect files under `modules/` 176 + - Add Darwin support by adding `nix-darwin` input 177 + - Explore the [Example template](/tutorials/example/) for namespaces and advanced features
+183
docs/src/content/docs/tutorials/example.md
···
··· 1 + --- 2 + title: "Template: Example" 3 + description: Feature showcase with namespaces, angle brackets, cross-platform aspects, and providers. 4 + --- 5 + 6 + The example template demonstrates Den's advanced features: namespaces, angle brackets, cross-platform hosts, bidirectional providers, and custom aspect libraries. 7 + 8 + ## Initialize 9 + 10 + ```console 11 + mkdir my-nix && cd my-nix 12 + nix flake init -t github:vic/den#example 13 + nix flake update den 14 + ``` 15 + 16 + ## Project Structure 17 + 18 + ``` 19 + flake.nix 20 + modules/ 21 + den.nix # host/home declarations 22 + dendritic.nix # flake-file + den wiring 23 + inputs.nix # flake inputs 24 + namespace.nix # creates the "eg" namespace 25 + tests.nix # CI checks 26 + vm.nix # VM runner 27 + aspects/ 28 + defaults.nix # global config + angle brackets demo 29 + alice.nix # user aspect 30 + igloo.nix # host aspect 31 + eg/ # namespace aspects 32 + autologin.nix 33 + ci-no-boot.nix 34 + routes.nix 35 + vm.nix 36 + vm-bootable.nix 37 + xfce-desktop.nix 38 + ``` 39 + 40 + ## Key Features Demonstrated 41 + 42 + ### Cross-Platform Hosts 43 + 44 + ```nix 45 + # modules/den.nix 46 + { 47 + den.hosts.x86_64-linux.igloo.users.alice = { }; 48 + den.hosts.aarch64-darwin.apple.users.alice = { }; 49 + den.homes.x86_64-linux.alice = { }; 50 + } 51 + ``` 52 + 53 + 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. 54 + 55 + ### Namespaces 56 + 57 + ```nix 58 + # modules/namespace.nix 59 + { inputs, den, ... }: 60 + { 61 + imports = [ (inputs.den.namespace "eg" true) ]; 62 + _module.args.__findFile = den.lib.__findFile; 63 + } 64 + ``` 65 + 66 + 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`. 67 + 68 + ### Namespace Aspects 69 + 70 + The `eg/` directory defines reusable aspects under the `eg` namespace: 71 + 72 + ```nix 73 + # modules/aspects/eg/vm.nix 74 + { eg, ... }: 75 + { 76 + eg.vm.provides = { 77 + gui.includes = [ eg.vm eg.vm-bootable._.gui eg.xfce-desktop ]; 78 + tui.includes = [ eg.vm eg.vm-bootable._.tui ]; 79 + }; 80 + } 81 + ``` 82 + 83 + Aspects can have nested **provides** — `eg.vm._.gui` and `eg.vm._.tui` are sub-aspects accessed via the `_.` provider syntax. 84 + 85 + ### Angle Brackets 86 + 87 + ```nix 88 + # modules/aspects/defaults.nix 89 + { den, __findFile ? __findFile, ... }: 90 + { 91 + den.default.includes = [ 92 + <eg/routes> # resolves to eg.routes 93 + <den/define-user> # resolves to den.provides.define-user 94 + ]; 95 + } 96 + ``` 97 + 98 + The `<name>` syntax is shorthand for aspect lookup. `<den/define-user>` resolves to `den.provides.define-user`. See [Angle Brackets](/guides/angle-brackets/). 99 + 100 + ### Bidirectional Providers 101 + 102 + ```nix 103 + # modules/aspects/alice.nix — user provides config TO the host 104 + den.aspects.alice = { 105 + provides.igloo = { host, ... }: { 106 + nixos.programs.nh.enable = host.name == "igloo"; 107 + }; 108 + }; 109 + 110 + # modules/aspects/igloo.nix — host provides config TO the user 111 + den.aspects.igloo = { 112 + provides.alice = { user, ... }: { 113 + homeManager.programs.helix.enable = user.name == "alice"; 114 + }; 115 + }; 116 + ``` 117 + 118 + 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. 119 + 120 + ### Custom Routes 121 + 122 + ```nix 123 + # modules/aspects/eg/routes.nix 124 + { den, ... }: 125 + { 126 + eg.routes = let 127 + inherit (den.lib) parametric; 128 + mutual = from: to: den.aspects.${from.aspect}._.${to.aspect} or { }; 129 + routes = { host, user, ... }@ctx: 130 + parametric.fixedTo ctx { 131 + includes = [ (mutual user host) (mutual host user) ]; 132 + }; 133 + in routes; 134 + } 135 + ``` 136 + 137 + This aspect **wires bidirectional providers** between hosts and users automatically. Including `<eg/routes>` in `den.default` activates all `provides` declarations. 138 + 139 + ### Tests 140 + 141 + ```nix 142 + # modules/tests.nix — verifies the template works 143 + { 144 + checks."alice enabled igloo nh" = checkCond "..." igloo.programs.nh.enable; 145 + checks."igloo enabled alice helix" = checkCond "..." alice-at-igloo.programs.helix.enable; 146 + } 147 + ``` 148 + 149 + ## Data Flow 150 + 151 + ```mermaid 152 + graph TD 153 + D["den.nix: igloo/apple + alice"] --> CH["ctx.host"] 154 + CH --> AI["aspects.igloo<br/>nixos + provides.alice"] 155 + CH --> CU["ctx.user"] 156 + CU --> AA["aspects.alice<br/>includes + provides.igloo"] 157 + AA -->|"routes: mutual"| AI 158 + AI -->|"routes: mutual"| AA 159 + CH --> HM["ctx.hm-host"] 160 + AI --> NixOS["nixosConfigurations.igloo"] 161 + AA --> NixOS 162 + AI --> Darwin["darwinConfigurations.apple"] 163 + AA --> Darwin 164 + ``` 165 + 166 + ## What It Provides 167 + 168 + | Feature | Provided | 169 + |---------|:--------:| 170 + | NixOS + Darwin hosts | ✓ | 171 + | Home-Manager | ✓ | 172 + | Standalone HM config | ✓ | 173 + | Namespaces (`eg`) | ✓ | 174 + | Angle brackets | ✓ | 175 + | Bidirectional providers | ✓ | 176 + | VM testing | ✓ | 177 + | CI checks | ✓ | 178 + 179 + ## Next Steps 180 + 181 + - Study the `eg/` namespace aspects as examples for your own libraries 182 + - Read [Namespaces](/guides/namespaces/) and [Angle Brackets](/guides/angle-brackets/) 183 + - Explore the [CI Tests template](/tutorials/ci/) for comprehensive feature coverage
-114
docs/src/content/docs/tutorials/first-aspect.md
··· 1 - --- 2 - title: Your First Aspect 3 - description: Create a cross-class, reusable Nix configuration aspect. 4 - --- 5 - 6 - ## What is an Aspect? 7 - 8 - An aspect is a composable bundle of Nix configurations that can span multiple 9 - classes (NixOS, Darwin, Home-Manager). Each host, user, and home you declare 10 - automatically gets an aspect. 11 - 12 - ## Step 1: Declare Your Infrastructure 13 - 14 - Start with a host and user: 15 - 16 - ```nix 17 - # modules/hosts.nix 18 - { 19 - den.hosts.x86_64-linux.igloo.users.tux = { }; 20 - } 21 - ``` 22 - 23 - Den creates `den.aspects.igloo` (host) and `den.aspects.tux` (user) automatically. 24 - 25 - ## Step 2: Configure the Host Aspect 26 - 27 - Any module file can contribute to any aspect. Create a file for your host: 28 - 29 - ```nix 30 - # modules/igloo.nix 31 - { den, ... }: { 32 - den.aspects.igloo = { 33 - nixos.networking.hostName = "igloo"; 34 - nixos.time.timeZone = "UTC"; 35 - homeManager.programs.direnv.enable = true; 36 - }; 37 - } 38 - ``` 39 - 40 - The `nixos` settings apply to NixOS. The `homeManager` settings apply 41 - to **every user** on this host. 42 - 43 - ## Step 3: Configure the User Aspect 44 - 45 - ```nix 46 - # modules/vic.nix 47 - { den, ... }: { 48 - den.aspects.tux = { 49 - homeManager.programs.fish.enable = true; 50 - nixos.users.users.tux.description = "Tux the Penguin"; 51 - }; 52 - } 53 - ``` 54 - 55 - User aspects contribute to **every host** that has this user. 56 - If `tux` exists on both `igloo` and another host, both get the fish config. 57 - 58 - ## Step 4: Use Includes for Composition 59 - 60 - Aspects can include other aspects: 61 - 62 - ```nix 63 - # modules/gaming.nix 64 - { den, ... }: { 65 - den.aspects.gaming = { 66 - nixos.programs.steam.enable = true; 67 - homeManager.programs.mangohud.enable = true; 68 - }; 69 - 70 - den.aspects.igloo.includes = [ den.aspects.gaming ]; 71 - } 72 - ``` 73 - 74 - ## Step 5: Use Provides for Nesting 75 - 76 - Organize related aspects in a tree: 77 - 78 - ```nix 79 - { den, ... }: { 80 - den.aspects.tools.provides.editors = { 81 - homeManager.programs.vim.enable = true; 82 - }; 83 - 84 - den.aspects.tux.includes = [ den.aspects.tools._.editors ]; 85 - } 86 - ``` 87 - 88 - The `._. ` syntax is shorthand for `.provides.`. 89 - 90 - ## Step 6: Set Global Defaults 91 - 92 - Use `den.default` for settings shared across all hosts and users: 93 - 94 - ```nix 95 - # modules/defaults.nix 96 - { 97 - den.default.homeManager.home.stateVersion = "25.11"; 98 - den.default.nixos.system.stateVersion = "25.11"; 99 - } 100 - ``` 101 - 102 - ## What You've Learned 103 - 104 - - [Aspects](/explanation/aspects/) bundle cross-class configs (nixos, darwin, homeManager) 105 - - Host aspects apply to all users on that host 106 - - User aspects apply to all hosts with that user 107 - - `includes` compose aspects together 108 - - `provides` creates nested aspect trees 109 - - [`den.default`](/explanation/context-pipeline/#dendefault-is-an-alias) sets global shared values 110 - 111 - ## Next 112 - 113 - [Context-Aware Configs](/tutorials/context-aware/) — make your aspects 114 - respond dynamically to their host and user context.
···
-97
docs/src/content/docs/tutorials/getting-started.md
··· 1 - --- 2 - title: Getting Started 3 - description: Set up your first Den project from scratch. 4 - --- 5 - 6 - ## Prerequisites 7 - 8 - - [Nix](https://nixos.org/download) with flakes enabled (or use [without flakes](/guides/no-flakes/)) 9 - - Basic familiarity with Nix modules 10 - 11 - ## Quick Start — Launch the Demo VM 12 - 13 - Try Den instantly without installing anything: 14 - 15 - ```console 16 - nix run github:vic/den 17 - ``` 18 - 19 - ## Initialize a New Project 20 - 21 - Create a fresh Den-based flake: 22 - 23 - ```console 24 - mkdir my-infra && cd my-infra 25 - nix flake init -t github:vic/den 26 - nix flake update den 27 - ``` 28 - 29 - This creates a project with: 30 - 31 - ``` 32 - flake.nix # inputs and entry point 33 - modules/ # your Den modules go here 34 - hosts.nix # host and user declarations 35 - aspects/ # aspect definitions 36 - ``` 37 - 38 - ## Your flake.nix 39 - 40 - The generated `flake.nix` imports Den and uses `import-tree` to load all modules: 41 - 42 - ```nix 43 - { 44 - outputs = inputs: 45 - inputs.flake-parts.lib.mkFlake { inherit inputs; } 46 - (inputs.import-tree ./modules); 47 - 48 - inputs = { 49 - den.url = "github:vic/den"; 50 - flake-aspects.url = "github:vic/flake-aspects"; 51 - import-tree.url = "github:vic/import-tree"; 52 - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 53 - flake-parts.url = "github:hercules-ci/flake-parts"; 54 - }; 55 - } 56 - ``` 57 - 58 - ## Declare a Host 59 - 60 - In `modules/hosts.nix`, declare your first host: 61 - 62 - ```nix 63 - { 64 - den.hosts.x86_64-linux.my-laptop.users.vic = { }; 65 - } 66 - ``` 67 - 68 - This single line creates: 69 - - A NixOS host named `my-laptop` on `x86_64-linux` 70 - - A user `vic` with Home-Manager support 71 - - Aspects `den.aspects.my-laptop` and `den.aspects.vic` 72 - 73 - ## Build It 74 - 75 - ```console 76 - nixos-rebuild switch --flake .#my-laptop 77 - ``` 78 - 79 - ## Available Templates 80 - 81 - | Template | Description | 82 - |----------|-------------| 83 - | `default` | Batteries-included layout | 84 - | `minimal` | Minimalistic Den flake | 85 - | `noflake` | No flakes, no flake-parts | 86 - | `example` | Examples and patterns | 87 - | `ci` | Feature test suite | 88 - | `bogus` | Bug reproduction | 89 - 90 - ```console 91 - nix flake init -t github:vic/den#minimal 92 - ``` 93 - 94 - ## Next Steps 95 - 96 - - [Your First Aspect](/tutorials/first-aspect/) — write cross-class configs 97 - - [Context-Aware Configs](/tutorials/context-aware/) — make aspects react to context
···
+118
docs/src/content/docs/tutorials/minimal.md
···
··· 1 + --- 2 + title: "Template: Minimal" 3 + description: The smallest possible Den setup — one host, one user, no extra dependencies. 4 + --- 5 + 6 + The minimal template demonstrates Den's core with zero extra dependencies beyond nixpkgs. 7 + 8 + ## Initialize 9 + 10 + ```console 11 + mkdir my-nix && cd my-nix 12 + nix flake init -t github:vic/den#minimal 13 + nix flake update den 14 + ``` 15 + 16 + ## Project Structure 17 + 18 + ``` 19 + flake.nix 20 + modules/ 21 + den.nix 22 + ``` 23 + 24 + ## flake.nix 25 + 26 + ```nix 27 + { 28 + outputs = inputs: 29 + (inputs.nixpkgs.lib.evalModules { 30 + modules = [ (inputs.import-tree ./modules) ]; 31 + specialArgs = { inherit inputs; }; 32 + }).config.flake; 33 + 34 + inputs = { 35 + nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; 36 + import-tree.url = "github:vic/import-tree"; 37 + flake-aspects.url = "github:vic/flake-aspects"; 38 + den.url = "github:vic/den"; 39 + }; 40 + } 41 + ``` 42 + 43 + Key points: 44 + - **No flake-parts** — uses `lib.evalModules` directly 45 + - **import-tree** — recursively loads all `.nix` files under `modules/` 46 + - **Results** land in `.config.flake` (nixosConfigurations, etc.) 47 + 48 + ## modules/den.nix 49 + 50 + ```nix 51 + { inputs, den, ... }: 52 + { 53 + imports = [ inputs.den.flakeModule ]; 54 + 55 + den.hosts.x86_64-linux.igloo.users.tux = { }; 56 + 57 + den.aspects.igloo = { 58 + nixos = { pkgs, ... }: { 59 + environment.systemPackages = [ pkgs.hello ]; 60 + boot.loader.grub.enable = false; # TODO: remove for real hardware 61 + fileSystems."/".device = "/dev/null"; 62 + }; 63 + }; 64 + 65 + den.aspects.tux = { 66 + includes = [ den.provides.primary-user ]; 67 + }; 68 + } 69 + ``` 70 + 71 + ### What This Declares 72 + 73 + 1. **One host** named `igloo` on `x86_64-linux` with **one user** named `tux` 74 + 2. **Host aspect** `den.aspects.igloo` — provides NixOS config (a package + boot/fs stubs) 75 + 3. **User aspect** `den.aspects.tux` — includes the [`primary-user`](/reference/batteries/#primary-user) battery 76 + 77 + ### How It Works 78 + 79 + ```mermaid 80 + graph TD 81 + H["den.hosts.x86_64-linux.igloo"] -->|"ctx.host"| A["den.aspects.igloo"] 82 + H -->|"ctx.user"| U["den.aspects.tux"] 83 + U -->|"includes"| PU["den.provides.primary-user"] 84 + A -->|"resolve nixos"| NixOS["nixosConfigurations.igloo"] 85 + ``` 86 + 87 + 1. Den reads `den.hosts` and creates a `ctx.host { host = igloo }` context 88 + 2. The host aspect `igloo` is activated with its NixOS config 89 + 3. For each user, a `ctx.user { host, user }` context is created 90 + 4. The user aspect `tux` includes `primary-user` (adds wheel/networkmanager groups) 91 + 5. Everything resolves into `nixosConfigurations.igloo` 92 + 93 + ## Build It 94 + 95 + ```console 96 + nix build .#nixosConfigurations.igloo.config.system.build.toplevel 97 + ``` 98 + 99 + ## Customize 100 + 101 + 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. 102 + 103 + ## What It Provides 104 + 105 + | Feature | Provided | 106 + |---------|:--------:| 107 + | NixOS host configuration | ✓ | 108 + | User with wheel/networkmanager | ✓ | 109 + | Home-Manager | ✗ | 110 + | Darwin support | ✗ | 111 + | Namespaces | ✗ | 112 + | VM testing | ✗ | 113 + 114 + ## Next Steps 115 + 116 + - Add more aspects in separate files under `modules/` 117 + - Graduate to the [Default template](/tutorials/default/) for Home-Manager support 118 + - Read [Core Principles](/explanation/core-principles/) to understand the context pipeline
+130
docs/src/content/docs/tutorials/noflake.md
···
··· 1 + --- 2 + title: "Template: No-Flake" 3 + description: Using Den with stable Nix — no flakes needed. 4 + --- 5 + 6 + 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. 7 + 8 + ## Initialize 9 + 10 + Since this template doesn't use flakes, clone it manually: 11 + 12 + ```console 13 + mkdir my-nix && cd my-nix 14 + cp -r $(nix eval --raw 'github:vic/den#templates.noflake.path')/* . 15 + npins update den 16 + ``` 17 + 18 + Or use `nix flake init` if you have flakes enabled: 19 + 20 + ```console 21 + nix flake init -t github:vic/den#noflake 22 + ``` 23 + 24 + ## Project Structure 25 + 26 + ``` 27 + default.nix # entry point (replaces flake.nix) 28 + with-inputs.nix # input resolver (replaces flake.lock) 29 + npins/ 30 + default.nix # npins fetcher 31 + sources.json # pinned dependencies 32 + modules/ 33 + den.nix # host/user declarations + aspects 34 + ``` 35 + 36 + ## File by File 37 + 38 + ### default.nix — Entry Point 39 + 40 + ```nix 41 + let 42 + outputs = inputs: 43 + (inputs.nixpkgs.lib.evalModules { 44 + modules = [ (inputs.import-tree ./modules) ]; 45 + specialArgs = { 46 + inherit inputs; 47 + inherit (inputs) self; 48 + }; 49 + }).config; 50 + in 51 + import ./with-inputs.nix outputs 52 + ``` 53 + 54 + This is the noflake equivalent of `flake.nix`. It uses `lib.evalModules` directly — the same mechanism Den uses internally. 55 + 56 + ### with-inputs.nix — Input Resolution 57 + 58 + 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. 59 + 60 + ### modules/den.nix — Configuration 61 + 62 + ```nix 63 + { inputs, ... }: 64 + { 65 + imports = [ inputs.den.flakeModule ]; 66 + 67 + den.default.nixos = { 68 + fileSystems."/".device = "/dev/fake"; 69 + boot.loader.grub.enable = false; 70 + }; 71 + 72 + den.hosts.x86_64-linux.igloo.users.tux = { }; 73 + 74 + den.aspects.igloo = { 75 + nixos = { pkgs, ... }: { 76 + environment.systemPackages = [ pkgs.vim ]; 77 + }; 78 + }; 79 + 80 + den.aspects.tux = { 81 + nixos = { 82 + imports = [ inputs.nix-maid.nixosModules.default ]; 83 + users.users.tux = { 84 + isNormalUser = true; 85 + maid.file.home.".gitconfig".text = '' 86 + [user] 87 + name=Tux 88 + ''; 89 + }; 90 + }; 91 + }; 92 + } 93 + ``` 94 + 95 + Notable differences from the flake templates: 96 + - Uses [nix-maid](https://github.com/vic/nix-maid) instead of Home-Manager (lighter alternative) 97 + - User config is done directly in `nixos` class (no HM class needed) 98 + 99 + ## Build 100 + 101 + ```console 102 + npins update den 103 + nixos-rebuild build --file . -A flake.nixosConfigurations.igloo 104 + ``` 105 + 106 + Or with `nix-build`: 107 + 108 + ```console 109 + nix-build -A flake.nixosConfigurations.igloo.config.system.build.toplevel 110 + ``` 111 + 112 + :::note 113 + 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. 114 + ::: 115 + 116 + ## What It Provides 117 + 118 + | Feature | Provided | 119 + |---------|:--------:| 120 + | NixOS host configuration | ✓ | 121 + | No flakes required | ✓ | 122 + | npins dependency pinning | ✓ | 123 + | nix-maid (HM alternative) | ✓ | 124 + | Home-Manager | ✗ (use nix-maid) | 125 + | flake-parts | ✗ | 126 + 127 + ## Next Steps 128 + 129 + - Read [Use Without Flakes](/guides/no-flakes/) for more details on flake-free usage 130 + - Consider the [Minimal template](/tutorials/minimal/) if you want flakes but not flake-parts
+61
docs/src/content/docs/tutorials/overview.md
···
··· 1 + --- 2 + title: Templates Overview 3 + description: All available Den templates and when to use each one. 4 + --- 5 + 6 + Den ships six templates that cover progressively complex setups. Use `nix flake init` to scaffold a new project: 7 + 8 + ```console 9 + nix flake init -t github:vic/den#<template> 10 + ``` 11 + 12 + ## Choosing a Template 13 + 14 + | Template | Use case | Flakes | flake-parts | Home-Manager | 15 + |----------|----------|:------:|:-----------:|:------------:| 16 + | [**minimal**](/tutorials/minimal/) | Smallest possible Den setup | ✓ | ✗ | ✗ | 17 + | [**default**](/tutorials/default/) | Recommended starting point | ✓ | ✓ | ✓ | 18 + | [**example**](/tutorials/example/) | Feature showcase with namespaces | ✓ | ✓ | ✓ | 19 + | [**noflake**](/tutorials/noflake/) | Stable Nix, no flakes | ✗ | ✗ | ✗ | 20 + | [**bogus**](/tutorials/bogus/) | Bug reproduction | ✓ | ✓ | ✓ | 21 + | [**ci**](/tutorials/ci/) | Den's own test suite | ✓ | ✓ | ✓ | 22 + 23 + ## Quick Start 24 + 25 + ```console 26 + mkdir my-nix && cd my-nix 27 + nix flake init -t github:vic/den 28 + nix flake update den 29 + ``` 30 + 31 + This clones the **default** template. Edit `modules/hosts.nix` to declare your machines, then: 32 + 33 + ```console 34 + nix run .#vm 35 + ``` 36 + 37 + ## Project Structure 38 + 39 + Every template follows the same pattern: 40 + 41 + ``` 42 + flake.nix # or default.nix for noflake 43 + modules/ 44 + den.nix # host/user declarations + den.flakeModule import 45 + *.nix # aspect definitions, one concern per file 46 + ``` 47 + 48 + 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. 49 + 50 + ## What Each Template Demonstrates 51 + 52 + - **minimal** — The absolute minimum: one host, one user, no extra dependencies 53 + - **default** — Production-ready structure with Home-Manager, VM testing, dendritic flake-file 54 + - **example** — Namespaces, angle brackets, cross-platform (NixOS + Darwin), providers 55 + - **noflake** — Using Den with npins instead of flakes 56 + - **bogus** — Creating minimal reproductions for bug reports with nix-unit 57 + - **ci** — Comprehensive tests covering every Den feature (your best learning resource) 58 + 59 + ## Next Steps 60 + 61 + Start with the [Minimal template](/tutorials/minimal/) to understand Den's core, then graduate to [Default](/tutorials/default/) for a real setup.
+1 -1
modules/aspects/defaults.nix
··· 4 { 5 imports = [ (lib.mkAliasOptionModule [ "default" ] [ "ctx" "default" ]) ]; 6 7 - ctx.default.conf = _: { }; 8 9 }; 10 }
··· 4 { 5 imports = [ (lib.mkAliasOptionModule [ "default" ] [ "ctx" "default" ]) ]; 6 7 + ctx.default._.default = _: { }; 8 9 }; 10 }
+5 -5
modules/aspects/provides/home-manager/hm-integration.nix
··· 43 { 44 den.provides.home-manager = { }; 45 46 - den.ctx.home.desc = "Standalone Home-Manager config provided by home aspect"; 47 - den.ctx.home.conf = { home }: parametric.fixedTo { inherit home; } den.aspects.${home.aspect}; 48 den.ctx.home.into.default = lib.singleton; 49 50 den.ctx.hm-host.into.hm-user = intoHmUsers; 51 - den.ctx.hm-user.desc = "(internal)"; 52 - den.ctx.hm-user.conf = forwardedToHost; 53 54 - den.ctx.hm-internal-user.conf = 55 { host, user }: 56 { class, aspect-chain }: 57 {
··· 43 { 44 den.provides.home-manager = { }; 45 46 + den.ctx.home.description = "Standalone Home-Manager config provided by home aspect"; 47 + den.ctx.home._.home = { home }: parametric.fixedTo { inherit home; } den.aspects.${home.aspect}; 48 den.ctx.home.into.default = lib.singleton; 49 50 den.ctx.hm-host.into.hm-user = intoHmUsers; 51 + den.ctx.hm-user.description = "(internal)"; 52 + den.ctx.hm-user._.hm-user = forwardedToHost; 53 54 + den.ctx.hm-internal-user._.hm-internal-user = 55 { host, user }: 56 { class, aspect-chain }: 57 {
+2 -2
modules/aspects/provides/home-manager/hm-os.nix
··· 43 { 44 den.ctx.host.into.hm-host = hm-detect; 45 46 - den.ctx.hm-host.desc = description; 47 - den.ctx.hm-host.conf = 48 { host }: 49 { 50 ${host.class}.imports = [ (hm-module host) ];
··· 43 { 44 den.ctx.host.into.hm-host = hm-detect; 45 46 + den.ctx.hm-host.description = description; 47 + den.ctx.hm-host._.hm-host = 48 { host }: 49 { 50 ${host.class}.imports = [ (hm-module host) ];
+10 -13
modules/context/os.nix
··· 2 let 3 inherit (den.lib.parametric) fixedTo atLeast; 4 5 - ctx.host.desc = "OS"; 6 - ctx.host.conf = { host }: fixedTo { inherit host; } den.aspects.${host.aspect}; 7 8 ctx.host.into.default = lib.singleton; 9 ctx.host.into.user = { host }: map (user: { inherit host user; }) (lib.attrValues host.users); 10 11 - ctx.user.desc = "OS user"; 12 - ctx.user.conf = 13 { host, user }@ctx: 14 { 15 - includes = 16 - let 17 - hostAspect = den.aspects.${host.aspect}; 18 - userAspect = den.aspects.${user.aspect}; 19 - in 20 - [ 21 - (fixedTo ctx userAspect) 22 - (atLeast hostAspect ctx) 23 - ]; 24 }; 25 26 ctx.user.into.default = lib.singleton;
··· 2 let 3 inherit (den.lib.parametric) fixedTo atLeast; 4 5 + ctx.host.description = "OS"; 6 + ctx.host._.host = { host }: fixedTo { inherit host; } den.aspects.${host.aspect}; 7 + ctx.host._.user = 8 + { host, user }@ctx: 9 + { 10 + includes = [ (atLeast den.aspects.${host.aspect} ctx) ]; 11 + }; 12 13 ctx.host.into.default = lib.singleton; 14 ctx.host.into.user = { host }: map (user: { inherit host user; }) (lib.attrValues host.users); 15 16 + ctx.user.description = "OS user"; 17 + ctx.user._.user = 18 { host, user }@ctx: 19 { 20 + includes = [ (fixedTo ctx den.aspects.${user.aspect}) ]; 21 }; 22 23 ctx.user.into.default = lib.singleton;
+38 -47
modules/context/types.nix
··· 1 { den, lib, ... }: 2 let 3 inherit (den.lib) parametric; 4 - inherit (den.lib.aspects.types) providerType; 5 6 ctxType = lib.types.submodule ( 7 - { name, ... }: 8 { 9 - freeformType = lib.types.lazyAttrsOf lib.types.deferredModule; 10 - options = { 11 - name = lib.mkOption { 12 - description = "Context type name"; 13 - type = lib.types.str; 14 - default = name; 15 - }; 16 - desc = lib.mkOption { 17 - description = "Context description"; 18 - type = lib.types.str; 19 - default = ""; 20 - }; 21 - conf = lib.mkOption { 22 - description = "Obtain a configuration aspect for context"; 23 - type = lib.types.functionTo providerType; 24 - default = { }; 25 - }; 26 - into = lib.mkOption { 27 - description = "Context transformations"; 28 - type = lib.types.lazyAttrsOf (lib.types.functionTo (lib.types.listOf lib.types.raw)); 29 - default = { }; 30 - }; 31 - includes = lib.mkOption { 32 - description = "Parametric aspects to include for this context"; 33 - type = lib.types.listOf providerType; 34 - default = [ ]; 35 - }; 36 - __functor = lib.mkOption { 37 - description = "Apply context with dedup across into targets."; 38 - type = lib.types.functionTo (lib.types.functionTo providerType); 39 - readOnly = true; 40 - internal = true; 41 - visible = false; 42 - default = ctxApply; 43 - }; 44 }; 45 } 46 ); 47 48 cleanCtx = 49 - ctx: 50 - builtins.removeAttrs ctx [ 51 "name" 52 - "desc" 53 - "conf" 54 "into" 55 "__functor" 56 ]; 57 58 collectPairs = 59 - self: ctx: 60 [ 61 { 62 - inherit ctx; 63 ctxDef = self; 64 } 65 ] 66 ++ lib.concatLists ( 67 - lib.mapAttrsToList (n: into: lib.concatMap (v: collectPairs den.ctx.${n} v) (into ctx)) self.into 68 ); 69 70 dedupIncludes = 71 let 72 go = 73 acc: remaining: 74 if remaining == [ ] then ··· 80 n = p.ctxDef.name; 81 clean = cleanCtx p.ctxDef; 82 isFirst = !(acc.seen ? ${n}); 83 items = 84 if isFirst then 85 [ 86 (parametric.fixedTo p.ctx clean) 87 - (p.ctxDef.conf p.ctx) 88 ] 89 else 90 [ 91 (parametric.atLeast clean p.ctx) 92 - (p.ctxDef.conf p.ctx) 93 ]; 94 in 95 go { ··· 105 result = [ ]; 106 } pairs; 107 108 - ctxApply = self: ctx: { includes = dedupIncludes (collectPairs self ctx); }; 109 110 in 111 {
··· 1 { den, lib, ... }: 2 let 3 inherit (den.lib) parametric; 4 + inherit (den.lib.aspects.types) aspectSubmodule providerType; 5 6 ctxType = lib.types.submodule ( 7 + { name, config, ... }: 8 { 9 + imports = aspectSubmodule.getSubModules; 10 + options.into = lib.mkOption { 11 + description = "Context transformations to other context types"; 12 + type = lib.types.lazyAttrsOf (lib.types.functionTo (lib.types.listOf lib.types.raw)); 13 + default = { }; 14 }; 15 + config.__functor = lib.mkForce (ctxApply config.name); 16 } 17 ); 18 19 cleanCtx = 20 + self: 21 + builtins.removeAttrs self [ 22 "name" 23 + "description" 24 "into" 25 + "provides" 26 "__functor" 27 + "modules" 28 + "resolve" 29 + "_module" 30 + "_" 31 ]; 32 33 collectPairs = 34 + source: self: ctx: 35 [ 36 { 37 + inherit ctx source; 38 ctxDef = self; 39 } 40 ] 41 ++ lib.concatLists ( 42 + lib.mapAttrsToList ( 43 + n: into: lib.concatMap (v: collectPairs self den.ctx.${n} v) (into ctx) 44 + ) self.into 45 ); 46 47 dedupIncludes = 48 let 49 + crossProvider = 50 + p: 51 + let 52 + src = p.source; 53 + n = p.ctxDef.name; 54 + in 55 + if src == null then (_: { }) else src.provides.${n} or (_: { }); 56 + 57 go = 58 acc: remaining: 59 if remaining == [ ] then ··· 65 n = p.ctxDef.name; 66 clean = cleanCtx p.ctxDef; 67 isFirst = !(acc.seen ? ${n}); 68 + selfProvider = p.ctxDef.provides.${n} or (_: { }); 69 + cross = crossProvider p; 70 items = 71 if isFirst then 72 [ 73 (parametric.fixedTo p.ctx clean) 74 + (selfProvider p.ctx) 75 + (cross p.ctx) 76 ] 77 else 78 [ 79 (parametric.atLeast clean p.ctx) 80 + (selfProvider p.ctx) 81 + (cross p.ctx) 82 ]; 83 in 84 go { ··· 94 result = [ ]; 95 } pairs; 96 97 + ctxApply = ctxName: _self: ctx: { 98 + includes = dedupIncludes (collectPairs null den.ctx.${ctxName} ctx); 99 + }; 100 101 in 102 {
+4 -4
templates/ci/modules/features/context/apply-non-exact.nix
··· 3 flake.tests.ctx-non-exact.test-apply-non-exact-less = denTest ( 4 { den, funnyNames, ... }: 5 { 6 - den.ctx.foobar.desc = "{foo,bar} context"; 7 - den.ctx.foobar.conf = 8 # use atLeast if you get error: function called with unexpected argument 9 den.lib.take.atLeast ( 10 { foo, bar }: ··· 30 flake.tests.ctx-non-exact.test-apply-non-exact-more = denTest ( 31 { den, funnyNames, ... }: 32 { 33 - den.ctx.foobar.desc = "{foo,bar} context"; 34 - den.ctx.foobar.conf = 35 # use exactly if you want to restrict to not having more args 36 den.lib.take.exactly ( 37 { foo, bar }:
··· 3 flake.tests.ctx-non-exact.test-apply-non-exact-less = denTest ( 4 { den, funnyNames, ... }: 5 { 6 + den.ctx.foobar.description = "{foo,bar} context"; 7 + den.ctx.foobar._.foobar = 8 # use atLeast if you get error: function called with unexpected argument 9 den.lib.take.atLeast ( 10 { foo, bar }: ··· 30 flake.tests.ctx-non-exact.test-apply-non-exact-more = denTest ( 31 { den, funnyNames, ... }: 32 { 33 + den.ctx.foobar.description = "{foo,bar} context"; 34 + den.ctx.foobar._.foobar = 35 # use exactly if you want to restrict to not having more args 36 den.lib.take.exactly ( 37 { foo, bar }:
+2 -2
templates/ci/modules/features/context/apply.nix
··· 3 flake.tests.ctx.test-apply = denTest ( 4 { den, funnyNames, ... }: 5 { 6 - den.ctx.foobar.desc = "{foo,bar} context"; 7 - den.ctx.foobar.conf = 8 { foo, bar }: 9 { 10 funny.names = [
··· 3 flake.tests.ctx.test-apply = denTest ( 4 { den, funnyNames, ... }: 5 { 6 + den.ctx.foobar.description = "{foo,bar} context"; 7 + den.ctx.foobar._.foobar = 8 { foo, bar }: 9 { 10 funny.names = [
+128
templates/ci/modules/features/context/cross-provider.nix
···
··· 1 + { denTest, ... }: 2 + { 3 + flake.tests.ctx-cross-provider = { 4 + 5 + test-source-provides-target = denTest ( 6 + { 7 + den, 8 + lib, 9 + funnyNames, 10 + ... 11 + }: 12 + { 13 + den.ctx.parent.description = "{x} context"; 14 + den.ctx.parent._.parent = 15 + { x }: 16 + { 17 + funny.names = [ "parent-${x}" ]; 18 + }; 19 + den.ctx.parent._.child = 20 + { x, y }: 21 + { 22 + funny.names = [ "parent-for-child-${x}-${y}" ]; 23 + }; 24 + den.ctx.parent.into.child = 25 + { x }: 26 + [ 27 + { 28 + inherit x; 29 + y = "derived"; 30 + } 31 + ]; 32 + 33 + den.ctx.child._.child = 34 + { x, y }: 35 + { 36 + funny.names = [ "child-${y}" ]; 37 + }; 38 + 39 + expr = funnyNames (den.ctx.parent { x = "hello"; }); 40 + expected = [ 41 + "child-derived" 42 + "parent-for-child-hello-derived" 43 + "parent-hello" 44 + ]; 45 + } 46 + ); 47 + 48 + test-source-provider-per-target-value = denTest ( 49 + { 50 + den, 51 + lib, 52 + funnyNames, 53 + ... 54 + }: 55 + { 56 + den.ctx.src.description = "source"; 57 + den.ctx.src._.src = 58 + { x }: 59 + { 60 + funny.names = [ x ]; 61 + }; 62 + den.ctx.src._.dst = 63 + { x, i }: 64 + { 65 + funny.names = [ "src-for-${x}-${toString i}" ]; 66 + }; 67 + den.ctx.src.into.dst = 68 + { x }: 69 + [ 70 + { 71 + inherit x; 72 + i = 1; 73 + } 74 + { 75 + inherit x; 76 + i = 2; 77 + } 78 + ]; 79 + 80 + den.ctx.dst._.dst = 81 + { x, i }: 82 + { 83 + funny.names = [ "dst-${toString i}" ]; 84 + }; 85 + 86 + expr = funnyNames (den.ctx.src { x = "a"; }); 87 + expected = [ 88 + "a" 89 + "dst-1" 90 + "dst-2" 91 + "src-for-a-1" 92 + "src-for-a-2" 93 + ]; 94 + } 95 + ); 96 + 97 + test-no-cross-provider-when-absent = denTest ( 98 + { 99 + den, 100 + lib, 101 + funnyNames, 102 + ... 103 + }: 104 + { 105 + den.ctx.src.description = "source without cross-provider"; 106 + den.ctx.src._.src = 107 + { x }: 108 + { 109 + funny.names = [ x ]; 110 + }; 111 + den.ctx.src.into.dst = { x }: [ { y = x; } ]; 112 + 113 + den.ctx.dst._.dst = 114 + { y }: 115 + { 116 + funny.names = [ "dst-${y}" ]; 117 + }; 118 + 119 + expr = funnyNames (den.ctx.src { x = "val"; }); 120 + expected = [ 121 + "dst-val" 122 + "val" 123 + ]; 124 + } 125 + ); 126 + 127 + }; 128 + }
+7 -7
templates/ci/modules/features/context/custom-ctx.nix
··· 10 ... 11 }: 12 { 13 - den.ctx.greeting.desc = "{hello} context"; 14 - den.ctx.greeting.conf = 15 { hello }: 16 { 17 funny.names = [ hello ]; 18 }; 19 den.ctx.greeting.into.shout = { hello }: [ { shout = lib.toUpper hello; } ]; 20 21 - den.ctx.shout.conf = 22 { shout }: 23 { 24 funny.names = [ shout ]; ··· 35 test-ctx-includes-static-and-parametric = denTest ( 36 { den, funnyNames, ... }: 37 { 38 - den.ctx.foo.desc = "{foo} context"; 39 - den.ctx.foo.conf = 40 { foo }: 41 { 42 funny.names = [ foo ]; ··· 63 test-ctx-owned = denTest ( 64 { den, funnyNames, ... }: 65 { 66 - den.ctx.bar.desc = "{x} context"; 67 - den.ctx.bar.conf = 68 { x }: 69 { 70 funny.names = [ x ];
··· 10 ... 11 }: 12 { 13 + den.ctx.greeting.description = "{hello} context"; 14 + den.ctx.greeting._.greeting = 15 { hello }: 16 { 17 funny.names = [ hello ]; 18 }; 19 den.ctx.greeting.into.shout = { hello }: [ { shout = lib.toUpper hello; } ]; 20 21 + den.ctx.shout._.shout = 22 { shout }: 23 { 24 funny.names = [ shout ]; ··· 35 test-ctx-includes-static-and-parametric = denTest ( 36 { den, funnyNames, ... }: 37 { 38 + den.ctx.foo.description = "{foo} context"; 39 + den.ctx.foo._.foo = 40 { foo }: 41 { 42 funny.names = [ foo ]; ··· 63 test-ctx-owned = denTest ( 64 { den, funnyNames, ... }: 65 { 66 + den.ctx.bar.description = "{x} context"; 67 + den.ctx.bar._.bar = 68 { x }: 69 { 70 funny.names = [ x ];
+70
templates/ci/modules/features/context/named-provider.nix
···
··· 1 + { denTest, ... }: 2 + { 3 + flake.tests.ctx-named-provider = { 4 + 5 + test-self-named-provider = denTest ( 6 + { den, funnyNames, ... }: 7 + { 8 + den.ctx.greet.description = "{who} context"; 9 + den.ctx.greet._.greet = 10 + { who }: 11 + { 12 + funny.names = [ "hello-${who}" ]; 13 + }; 14 + 15 + expr = funnyNames (den.ctx.greet { who = "nix"; }); 16 + expected = [ "hello-nix" ]; 17 + } 18 + ); 19 + 20 + test-self-named-plus-owned = denTest ( 21 + { den, funnyNames, ... }: 22 + { 23 + den.ctx.greet.description = "{who} context"; 24 + den.ctx.greet._.greet = 25 + { who }: 26 + { 27 + funny.names = [ "hello-${who}" ]; 28 + }; 29 + den.ctx.greet.funny.names = [ "owned" ]; 30 + 31 + expr = funnyNames (den.ctx.greet { who = "nix"; }); 32 + expected = [ 33 + "hello-nix" 34 + "owned" 35 + ]; 36 + } 37 + ); 38 + 39 + test-named-provider-with-into = denTest ( 40 + { 41 + den, 42 + lib, 43 + funnyNames, 44 + ... 45 + }: 46 + { 47 + den.ctx.greet.description = "{who} context"; 48 + den.ctx.greet._.greet = 49 + { who }: 50 + { 51 + funny.names = [ who ]; 52 + }; 53 + den.ctx.greet.into.yell = { who }: [ { shout = lib.toUpper who; } ]; 54 + 55 + den.ctx.yell._.yell = 56 + { shout }: 57 + { 58 + funny.names = [ shout ]; 59 + }; 60 + 61 + expr = funnyNames (den.ctx.greet { who = "world"; }); 62 + expected = [ 63 + "WORLD" 64 + "world" 65 + ]; 66 + } 67 + ); 68 + 69 + }; 70 + }