···11# These are supported funding model platforms
2233-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
33+github: [vic] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
44patreon: # Replace with a single Patreon username
55open_collective: # Replace with a single Open Collective username
66ko_fi: oeiuwq # Replace with a single Ko-fi username
+75-423
README.md
···11-# vix - A Dendritic NixOS Configuration
22-33-A complete example of the **dendritic pattern** for NixOS, using [den](https://github.com/vic/den) to achieve composable, aspect-oriented system configuration.
11+# vix - Vic's Nix Environment - and dendritic example repo.
4255-This repository demonstrates how to structure a real-world NixOS setup using:
66-- [den](https://github.com/vic/den) - Dendritic configuration framework
77-- [flake-aspects](https://github.com/vic/flake-aspects) - Aspect-oriented programming for Nix
88-- [flake-file](https://github.com/vic/flake-file) - Auto-generated flake management
99-- [import-tree](https://github.com/vic/import-tree) - Automatic module discovery
33+My repo serves as an educational example showing how [den](https://github.com/vic/den) and my related [libs](https://vic.github.io/dendrix/Dendritic-Ecosystem.html#vics-dendritic-libraries) structure a [Dendritic](https://vic.github.io/dendrix/Dendritic.html) NixOS setup with **named, composable aspects** instead of file imports. This is just one of many possible ways to organize a dendritic implementation. Feel free to explore, and share how you do things.
1041111-## Try It Out
1251313-Experience the dendritic pattern immediately:
1414-```bash
1515-nix run github:vic/vix/den#vm
1616-```
66+This specific setup is powered by **[den](https://github.com/vic/den) · [flake-aspects](https://github.com/vic/flake-aspects) · [denful](https://github.com/vic/denful) · [flake-file](https://github.com/vic/flake-file) · [import-tree](https://github.com/vic/import-tree) · [flake-parts](https://github.com/hercules-ci/flake-parts)**
1771818-This launches a VM configured with the same dendritic structure as the production system.
88+--
1992020-## Understanding the Dendritic Structure
1010+## Aspects vs Imports
21112222-### 🎯 Core Concept: Aspects Over Modules
1212+```nix
1313+# Traditional imports # Dendritic aspects
1414+imports = [ vix.nargun.includes = [
1515+ ./hardware.nix vix.hardware
1616+ ../../shared/desktop.nix vix.niri-desktop
1717+]; ];
1818+```
23192424-Traditional NixOS configurations use modules that get imported into a monolithic tree. The dendritic pattern uses **aspects**—named, composable units that can be mixed and matched without creating deep import hierarchies.
2020+Aspects are named values, not file paths. They compose without relative path juggling.
25212626-### 📁 Repository Structure
2222+## How It's Wired
27232824```
2925modules/
3030-├── dendritic.nix # Entry point: initializes dendritic setup
3131-├── vix.nix # Custom aspect namespace
3232-├── hosts.nix # Host declarations & default profiles
3333-├── vm.nix # VM launcher for development
3434-│
3535-├── community/ # Reusable aspects (candidate for upstreaming)
3636-│ ├── profile.nix # Host/user profile hooks
3737-│ ├── unfree.nix # Aspect for unfree packages
3838-│ ├── autologin.nix # Parametric autologin aspect
3939-│ ├── *-desktop.nix # Desktop environment aspects
4040-│ └── dev-laptop.nix # Composed laptop aspect
4141-│
4242-├── vic/ # User-specific aspects
4343-│ ├── common-host-env.nix # User environment composition
4444-│ ├── admin.nix # User permissions aspect
4545-│ ├── fish.nix # Shell configuration aspect
4646-│ ├── dots.nix # Dotfile management aspect
4747-│ └── *.nix # Individual feature aspects
4848-│
4949-└── hosts/
5050- └── nargun.nix # Host-specific aspect composition with vm variant.
2626+├── dendritic.nix # Bootstraps dendritic libs
2727+├── community/vix.nix # Creates vix.* namespace
2828+├── hosts.nix # Declares hosts, wires default profiles
2929+├── community/
3030+│ └── profile.nix # Defines host-profile and user-profile hooks
3131+├── hosts/
3232+│ └── nargun.nix # Composes aspects for nargun host
3333+└── vic/
3434+ └── common-host-env.nix # Composes user environment aspects
5135```
52365353-## Key Benefits of This Structure
3737+## Key Patterns
54385555-### 1. **Namespace Isolation** ([`vix.nix`](modules/vix.nix))
5656-5757-Created my own aspect namespace without polluting the global scope:
5858-3939+### 1. Custom Namespace ([`vix.nix`](modules/community/vix.nix))
5940```nix
6060-# vix is actually: den.aspects.vix.provides.
6161-{ vix, ... }: { # read access
6262- vix.gaming = ...; # write access
6363-}
4141+den.aspects.vix.provides = { }; # you can also use flake.aspects to expose all to flake.modules.
4242+flake.vix = config.den.aspects.vix.provides; # I just want to expose the vix aspect tree.
4343+_module.args.vix = config.den.aspects.vix.provides;
6444```
65456666-**Benefit**: All my aspects live under `vix.*`, making it clear what's yours vs. what comes from den or other sources.
4646+Creates `vix.*` namespace. Everything under `vix` belongs to this config.
67476868-### 2. **Declarative Host Registration** ([`hosts.nix`](modules/hosts.nix))
4848+The `vix` namespace is written to directly, and read from module args, it is also shared as a flake output.
4949+Meaning other people can do `_module.args.vix = inputs.vix.vix`, and re-use modules from this repository's `community/` exactly as they are. Adopting a namespace for your aspects allows re-use for people using import-tree to load files from community repos.
69507070-Declare hosts and users in one place:
71517272-```nix
7373-den.hosts.x86_64-linux.nargun.users.vic = { };
7474-den.hosts.x86_64-linux.nargun-vm.users.vic = { };
7575-```
76527777-**Benefit**: Clear overview of your infrastructure. No hidden host definitions scattered across files.
7878-7979-### 3. **Profile Hooks for Automatic Composition** ([`profile.nix`](modules/community/profile.nix))
8080-8181-Set default includes for all hosts and users:
8282-5353+### 2. Central Host Declaration ([`hosts.nix`](modules/hosts.nix))
8354```nix
8484-den.default.host._.host.includes = [
8585- vix.host-profile
8686- den.home-manager
8787-];
5555+den.hosts.x86_64-linux.nargun.users.vic = { };
88568989-den.default.user._.user.includes = [
9090- vix.user-profile
9191-];
5757+den.default.host._.host.includes = [ vix.host-profile ];
5858+den.default.user._.user.includes = [ vix.user-profile ];
9259```
9393-9494-**Benefit**: Every host/user automatically gets base configuration without manual wiring. Add once, applies everywhere.
9595-9696-### 4. **Parametric Aspects** ([`profile.nix`](modules/community/profile.nix))
6060+All hosts declared here. Default profiles automatically applied to every host/user.
97619898-Profiles can dynamically select aspects based on host/user names:
9999-6262+### 3. Dynamic Profile Routing ([`profile.nix`](modules/community/profile.nix))
10063```nix
10164vix.host-profile = { host }: {
102102- includes = [
103103- (vix.${host.name} or { }) # Host-specific if exists
104104- (vix.host-name host) # Parametric hostname setter
105105- vix.state-version # Common to all hosts
106106- ];
6565+ # access host profile from vix, many modules can contribute to the same aspect.
6666+ includes = [ vix.${host.name} ];
10767};
108108-```
10968110110-**Benefit**: Generic code that adapts to context. No hardcoded names, fully reusable.
111111-112112-### 5. **Aspect Composition** ([`nargun.nix`](modules/hosts/nargun.nix))
113113-114114-Hosts are built by composing aspects:
115115-116116-```nix
117117-vix.nargun.includes = [
118118- vix.nargun._.base
119119- vix.nargun._.hw
120120-];
121121-122122-vix.nargun.provides = {
123123- hw.includes = [
124124- vix.mexico # Locale configuration
125125- vix.bootable # Boot loader setup
126126- vix.kvm-amd # Virtualization support
127127- vix.niri-desktop # Window manager
128128- vix.kde-desktop # Fallback desktop
129129- ];
130130-131131- base.includes = [
132132- vix.dev-laptop # Common laptop features
133133- ];
6969+vix.user-profile = { host, user }: {
7070+ # users can enhance the host they are part of, by providing aspects to it.
7171+ includes = [ (vix.${user.name}._.common-host-env { inherit host user; }) ];
13472};
13573```
136136-137137-**Benefit**:
138138-- Mix hardware configs, desktops, and features freely
139139-- Share common setups between real hardware and VM
140140-- Easy to see what a host includes at a glance
141141-142142-### 6. **Shared Configuration Between Host Variants** ([`nargun.nix`](modules/hosts/nargun.nix))
143143-144144-Production and VM hosts share a base:
145145-146146-```nix
147147-vix.nargun-vm.includes = [
148148- vix.nargun._.base # Shared configuration
149149- vix.nargun._.vm # VM-specific overrides
150150-];
151151-```
152152-153153-**Benefit**: Test your actual configuration in a VM with minimal differences. No "works on my VM" problems.
154154-155155-### 7. **Hierarchical Aspect Organization** ([`nargun.nix`](modules/hosts/nargun.nix))
156156-157157-Use `provides` to create sub-aspects:
7474+Profiles select aspects by host/user name. `vix.nargun` wired automatically for nargun host.
158757676+### 4. Aspect Composition with Variants ([`nargun.nix`](modules/hosts/nargun.nix))
15977```nix
16078vix.nargun.provides = {
161161- base.includes = [...]; # vix.nargun._.base
162162- hw.includes = [...]; # vix.nargun._.hw
163163- vm.includes = [...]; # vix.nargun._.vm
7979+ base.includes = [ vix.dev-laptop ];
8080+ hw.includes = [ vix.kvm-amd vix.niri-desktop ];
16481};
8282+8383+vix.nargun.includes = [ vix.nargun._.base vix.nargun._.hw ];
8484+vix.nargun-vm.includes = [ vix.nargun._.base vix.nargun._.vm ];
16585```
8686+Sub-aspects via `provides.X` become `_.X`. Hardware and VM share `base`.
16687167167-**Benefit**: Organize related configuration without creating separate files. The `_` makes sub-aspects non-recursive.
168168-169169-### 8. **User Environment Composition** ([`common-host-env.nix`](modules/vic/common-host-env.nix))
170170-171171-Compose user environments from small, focused aspects:
172172-8888+### 5. User Environment Assembly ([`common-host-env.nix`](modules/vic/common-host-env.nix))
17389```nix
17490vix.vic._.common-host-env = { host, user }: {
17591 includes = map (f: f { inherit host user; }) [
17692 vix.vic.provides.admin
17793 vix.vic.provides.fish
178178- vix.vic.provides.terminals
179179- vix.vic.provides.editors
180180- vix.vic.provides.doom-btw
181181- vix.vic.provides.vim-btw
18294 // ... more aspects
18395 ];
18496};
18597```
186186-187187-**Benefit**:
188188-- Each aspect is small, testable, focused
189189-- Easy to enable/disable features
190190-- Functions receive context (`host`, `user`) for parametric behavior
191191-192192-### 9. **Parametric, Generic Aspects** ([`admin.nix`](modules/vic/admin.nix), [`fish.nix`](modules/vic/fish.nix))
193193-194194-Aspects accept parameters instead of hardcoding values:
9898+User profile calls this with context. Each aspect receives `{ host, user }`.
19599100100+### 6. Multi-Class Aspects ([`fish.nix`](modules/vic/fish.nix))
196101```nix
197197-# admin.nix
198198-vix.vic.provides.admin = { user, ... }: {
199199- nixos.users.users.${user.userName} = {
200200- isNormalUser = true;
201201- extraGroups = [ "wheel" "networkmanager" ];
202202- };
203203-};
204204-205205-# fish.nix
206102vix.vic.provides.fish = { user, ... }: {
207103 nixos.users.users.${user.userName}.shell = pkgs.fish;
208104 homeManager.programs.fish.enable = true;
209105};
210106```
211211-212212-**Benefit**: Aspects are reusable across different users/hosts. Change username once in host declaration, everything updates.
213213-214214-### 10. **Multi-Class Aspects** ([`fish.nix`](modules/vic/fish.nix))
215215-216216-Single aspect can configure multiple "classes" (nixos, homeManager, darwin, etc.):
217217-218218-```nix
219219-vix.vic.provides.fish = { user, ... }: {
220220- nixos = { pkgs, ... }: {
221221- programs.fish.enable = true;
222222- users.users.${user.userName}.shell = pkgs.fish;
223223- };
224224-225225- homeManager = {
226226- programs.fish.enable = true;
227227- };
228228-};
229229-```
230230-231231-**Benefit**: Related configuration stays together. System-level and user-level config for one feature in one file.
232232-233233-### 11. **Reusable Community Aspects** ([`modules/community/`](modules/community/))
234234-235235-Structure promotes sharing:
236236-237237-```
238238-community/
239239-├── profile.nix # Integration hooks
240240-├── unfree.nix # Unfree package handling
241241-├── autologin.nix # Parametric autologin
242242-├── *-desktop.nix # Desktop environments
243243-└── dev-laptop.nix # Feature compositions
244244-```
245245-246246-**Benefit**:
247247-- Clear separation of reusable vs. personal
248248-- Easy to upstream to [denful](https://github.com/vic/denful)
249249-- Stop copy-pasting, start sharing
250250-251251-### 12. **Functional Aspects with Parameters** ([`unfree.nix`](modules/community/unfree.nix))
252252-253253-Aspects can be functions that return aspects:
254254-255255-```nix
256256-vix.unfree = allowed-names: {
257257- __functor = _: { class, aspect-chain }: {
258258- ${class}.nixpkgs.config.allowUnfreePredicate =
259259- pkg: builtins.elem (lib.getName pkg) allowed-names;
260260- };
261261-};
107107+Single aspect configures both system and home-manager.
262108263263-# Usage:
264264-vix.vic.provides.editors.includes = [
265265- (vix.unfree [ "cursor" "vscode" ])
266266-];
267267-```
109109+## The Flow
268110269269-**Benefit**: Create aspect factories. Same pattern, different parameters. No duplication.
111111+1. **[`dendritic.nix`](modules/dendritic.nix)** loads dendritic libs
112112+2. **[`vix.nix`](modules/vix.nix)** creates namespace (`vix.*`)
113113+3. **[`hosts.nix`](modules/hosts.nix)** declares hosts and wires profiles:
114114+ - `den.hosts.x86_64-linux.nargun.users.vic = { }`
115115+ - Every host includes `vix.host-profile`
116116+ - Every user includes `vix.user-profile`
117117+4. **[`profile.nix`](modules/community/profile.nix)** routes by name:
118118+ - `vix.host-profile` → `vix.${host.name}` (e.g., `vix.nargun`)
119119+ - `vix.user-profile` → `vix.${user.name}._.common-host-env`
120120+5. **[`nargun.nix`](modules/hosts/nargun.nix)** composes host aspects
121121+6. **[`common-host-env.nix`](modules/vic/common-host-env.nix)** composes user aspects
270122271271-### 13. **Development Workflow with VM** ([`vm.nix`](modules/vm.nix))
272272-273273-Package your system as a runnable VM:
274274-275275-```nix
276276-packages.vm = pkgs.writeShellApplication {
277277- name = "vm";
278278- text = ''
279279- ${inputs.self.nixosConfigurations.nargun-vm.config.system.build.vm}/bin/run-nargun-vm-vm "$@"
280280- '';
281281-};
282282-```
283283-284284-**Benefit**:
285285-- Test configuration changes without rebooting
286286-- Share exact system state with others via `nix run`
287287-- Rapid iteration during development
288288-289289-### 14. **Smart Dotfile Management** ([`dots.nix`](modules/vic/dots.nix))
290290-291291-Out-of-store symlinks for live editing:
292292-293293-```nix
294294-dotsLink = path:
295295- config.lib.file.mkOutOfStoreSymlink
296296- "${config.home.homeDirectory}/.flake/modules/vic/dots/${path}";
297297-298298-home.file.".config/nvim".source = dotsLink "config/nvim";
299299-```
300300-301301-**Benefit**: Edit dotfiles directly, changes reflect immediately without rebuilding. Best of both worlds.
302302-303303-## Getting Started with Dendritic
304304-305305-### 1. Bootstrap from Template
306306-307307-```bash
308308-nix flake init -t github:vic/flake-file#dendritic
309309-nix flake update
310310-```
311311-312312-This creates:
313313-- [`modules/dendritic.nix`](modules/dendritic.nix) - Den initialization
314314-- Basic structure following the pattern
315315-316316-### 2. Create Your Namespace ([`vix.nix`](modules/vix.nix))
317317-318318-```nix
319319-{ config, lib, ... }:
320320-{
321321- den.aspects.myname.provides = { };
322322- _module.args.myname = config.den.aspects.myname.provides;
323323- flake.myname = config.den.aspects.myname.provides;
324324- imports = [ (lib.mkAliasOptionModule [ "myname" ] [ "den" "aspects" "myname" "provides" ]) ];
325325-}
326326-```
327327-328328-### 3. Register Hosts ([`hosts.nix`](modules/hosts.nix))
329329-330330-```nix
331331-{ myname, den, ... }:
332332-{
333333- den.hosts.x86_64-linux.myhost.users.myuser = { };
334334-335335- den.default.host._.host.includes = [
336336- myname.host-profile
337337- den.home-manager
338338- ];
339339-340340- den.default.user._.user.includes = [
341341- myname.user-profile
342342- ];
343343-}
344344-```
345345-346346-### 4. Create Profile Hooks (see [`profile.nix`](modules/community/profile.nix))
347347-348348-```nix
349349-myname.host-profile = { host }: {
350350- includes = [
351351- (myname.${host.name} or { })
352352- myname.state-version
353353- ];
354354-};
355355-356356-myname.user-profile = { host, user }: {
357357- includes = [
358358- ((myname.${user.name}._.common-host-env or (_: { })) { inherit host user; })
359359- ];
360360-};
361361-```
362362-363363-### 5. Define Host Aspects (see [`nargun.nix`](modules/hosts/nargun.nix))
364364-365365-```nix
366366-{ myname, ... }:
367367-{
368368- myname.myhost.includes = [
369369- myname.bootable
370370- myname.networking
371371- myname.my-desktop
372372- ];
373373-}
374374-```
375375-376376-### 6. Build User Environment (see [`common-host-env.nix`](modules/vic/common-host-env.nix))
377377-378378-```nix
379379-myname.myuser._.common-host-env = { host, user }: {
380380- includes = map (f: f { inherit host user; }) [
381381- myname.myuser.provides.admin
382382- myname.myuser.provides.shell
383383- myname.myuser.provides.editor
384384- ];
385385-};
386386-```
387387-388388-## Design Principles
389389-390390-### Aspect Composition Over Module Imports
391391-392392-**Traditional NixOS:**
393393-```nix
394394-imports = [
395395- ./hardware.nix
396396- ./networking.nix
397397- ./desktop.nix
398398- ./users/vic.nix
399399-];
400400-```
401401-402402-**Dendritic Pattern:**
403403-```nix
404404-vix.nargun.includes = [
405405- vix.hardware
406406- vix.networking
407407- vix.desktop
408408-];
409409-```
410410-411411-Benefits:
412412-- Aspects are named and first-class
413413-- Can be referenced, composed, and parameterized
414414-- No relative path management
415415-- Clear dependency relationships
416416-417417-### Parametric by Default
418418-419419-All aspects should accept `{ host, user }` when relevant:
420420-421421-```nix
422422-# ❌ Hardcoded
423423-vix.vic.admin.nixos.users.users.vic = { ... };
424424-425425-# ✅ Parametric
426426-vix.vic.provides.admin = { user, ... }: {
427427- nixos.users.users.${user.userName} = { ... };
428428-};
429429-```
430430-431431-### Separate Personal from Shareable
432432-433433-- `modules/community/` - Reusable, generic, upstreamable
434434-- `modules/{yourname}/` - Personal, specific to your needs
435435-- `modules/hosts/` - Host-specific configuration
436436-437437-When something in your personal namespace becomes useful, move it to `community/` and consider upstreaming to [denful](https://github.com/vic/denful).
123123+Result: Declare a host in one place, everything wires automatically via naming convention.
438124439125## Learning Path
440126441441-Follow this sequence to understand the pattern:
442442-443443-1. **[`dendritic.nix`](modules/dendritic.nix)** - How den is initialized
444444-2. **[`vix.nix`](modules/vix.nix)** - Creating a custom namespace
445445-3. **[`hosts.nix`](modules/hosts.nix)** - Host registration and default hooks
446446-4. **[`profile.nix`](modules/community/profile.nix)** - Profile system for automatic composition
447447-5. **[`nargun.nix`](modules/hosts/nargun.nix)** - Host aspect composition example
448448-6. **[`common-host-env.nix`](modules/vic/common-host-env.nix)** - User environment composition
449449-7. **[`admin.nix`](modules/vic/admin.nix)**, **[`fish.nix`](modules/vic/fish.nix)** - Simple parametric aspects
450450-8. **[`unfree.nix`](modules/community/unfree.nix)** - Advanced: functional aspects
451451-9. **[`vm.nix`](modules/vm.nix)** - Development workflow
452452-453453-## Contributing to the Dendritic Ecosystem
454454-455455-Found something useful in this repo? Instead of copy-pasting:
456456-457457-1. Move it to `modules/community/` if it's not already there
458458-2. Make it parametric and generic
459459-3. Open an issue to discuss upstreaming to [denful](https://github.com/vic/denful)
460460-461461-The goal is to build a library of well-maintained, composable aspects that everyone can benefit from.
462462-463463-## Migration Notes
464464-465465-This repository represents a complete rewrite of vix using the dendritic pattern. The old monolithic approach is preserved in the `main` branch for reference.
466466-467467-**Development workflow:** Boot from stable `main` branch, edit this `den` branch, test changes via `nix run .#vm` without rebooting.
127127+Follow the flow above, then explore:
128128+- **[`fish.nix`](modules/vic/fish.nix)** - Simple parametric aspect
129129+- **[`unfree.nix`](modules/community/unfree.nix)** - Aspect factory pattern
130130+- **[`vm.nix`](modules/vm.nix)** - Package system as VM: `nix run github:vic/vix/den#vm`
468131469132## Why Dendritic?
470133471471-Traditional NixOS configurations grow into tangled import trees. You end up with:
472472-- Deep hierarchies hard to navigate
473473-- Unclear dependencies
474474-- Difficulty reusing configuration
475475-- Copy-paste proliferation
476134477477-The dendritic pattern solves this by:
478478-- **Named aspects** instead of file paths
479479-- **Composition** instead of inheritance
480480-- **Parameters** instead of hardcoded values
481481-- **Namespaces** instead of global scope
482482-- **Automatic discovery** via import-tree
483483-- **First-class functions** for aspect factories
135135+- Named aspects instead of manual imports.
484136485485-The result: configuration that's easier to understand, modify, test, and share.
137137+- Functional Composition instead of Duplication.
486138487487----
139139+- Parameters intead of Hardcoding.
488140489489-**See also:** [den documentation](https://github.com/vic/den), [flake-aspects](https://github.com/vic/flake-aspects), [denful](https://github.com/vic/denful)141141+- Sharing instead of Copy+Pasting.