···11+---
22+title: Host<-> User Bidirectional Configurations
33+description: How to configure bidirectional behavior in Den and how it differs from mutual-provider.
44+---
55+66+import { Aside } from '@astrojs/starlight/components';
77+88+<Aside title="Source" icon="github">
99+[`context/user.nix`](https://github.com/vic/den/blob/main/modules/context/user.nix) ·
1010+[`context/host.nix`](https://github.com/vic/den/blob/main/modules/context/host.nix) ·
1111+[`bidirectional.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/bidirectional.nix) ·
1212+[`mutual-provider.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/mutual-provider.nix)
1313+</Aside>
1414+1515+1616+## Normal, non-bidirectional OS configuration
1717+1818+Den framework is built around **context pipeline** transformations.
1919+In order to create a full OS configuration, everything starts with a host definition:
2020+2121+```nix "igloo" "tux"
2222+den.hostx.x86_64-linux.igloo.users.tux = {}
2323+```
2424+2525+We need to build the `nixos` Nix module that will later be used by `lib.nixosSystem`.
2626+To do so, Den invokes the `den.ctx.host` pipeline like this:
2727+2828+2929+```mermaid
3030+sequenceDiagram
3131+ participant Den
3232+ participant host as den.ctx.host
3333+ participant user as den.ctx.user
3434+ participant igloo as den.aspects.igloo
3535+ participant tux as den.aspects.tux
3636+3737+ Den ->> host : {host = igloo}
3838+3939+ host ->> igloo : request nixos class
4040+ igloo -->> igloo : each igloo.includes takes { host }
4141+ igloo -->> host : { nixos = ... } owned and parametric results
4242+4343+ host ->> user : fan-outs for each user: { host, user }
4444+4545+ user ->> tux : request nixos class
4646+ tux -->> tux : home classes forwarded as nixos class
4747+ tux -->> tux : each tux.includes takes { host, user }
4848+ tux -->> user : { nixos = ... } owned and parametric results
4949+5050+ user -->> host : { nixos = ... } all user contributions
5151+5252+ host -->> Den : complete nixos module for lib.nixosSystem
5353+5454+```
5555+5656+This is the normal NixOS pipeline an __Not Bidirectional__. All OS contributions come from
5757+the host itself and from each of its user.
5858+5959+6060+## What Bidirectionality means
6161+6262+__Bidirectionality__ means that not only a User contributes
6363+configuration to a Host, but **also** that a Host contributes
6464+configurations to a User.
6565+6666+This is useful when the Host wishes to provide a
6767+commmon home environment for its users.
6868+6969+## `den.provides.bidirectional`
7070+7171+Bidirectionality is enabled __per-user__ or for _all_ of them.
7272+7373+```nix
7474+# only tux takes configurations from its hosts
7575+den.aspects.tux.includes = [ den._.bidirectional ];
7676+7777+# for ALL users
7878+den.ctx.user.includes = [ den._.bidirectional ];
7979+```
8080+8181+When Bidirectionality is enabled, the interaction looks like this:
8282+8383+```mermaid
8484+sequenceDiagram
8585+ participant Den
8686+ participant host as den.ctx.host
8787+ participant user as den.ctx.user
8888+ participant igloo as den.aspects.igloo
8989+ participant tux as den.aspects.tux
9090+9191+ Den ->> host : {host = igloo}
9292+9393+ host ->> igloo : request nixos class
9494+ igloo -->> igloo : each igloo.includes takes { host }
9595+ igloo -->> host : { nixos = ... } owned and parametric results
9696+9797+ host ->> user : fan-outs for each user: { host, user }
9898+9999+ user ->> tux : request nixos class
100100+101101+ tux ->> igloo : request home class
102102+ igloo -->> igloo : each igloo.includes takes { host, user }
103103+ igloo -->> tux : { hjem = ... } owned and parametric results
104104+105105+ tux -->> tux : home classes forwarded as nixos class
106106+107107+ tux -->> tux : each tux.includes takes { host, user }
108108+ tux -->> user : { nixos = ... } owned and parametric results
109109+110110+ user -->> host : { nixos = ... } all user contributions
111111+112112+ host -->> Den : complete nixos module for lib.nixosSystem
113113+114114+```
115115+116116+Crucial points here are `igloo.includes takes { host }` and `igloo.includes takes { host, user }`.
117117+118118+Because the list of aspects at `igloo.includes` get invoked twice, with different contexts,
119119+functions at `igloo.includes` must take care of the following:
120120+121121+```nix
122122+# use den.lib.take.exactly to avoid being called with `{host, user}`
123123+take.exactly ({ host }: ...)
124124+125125+# use den.lib.take.atLeast to avoid being called with `{host}`
126126+take.atLeast ({ host, user }: ...)
127127+```
128128+129129+Read the documentation at [`context/user.nix`](https://github.com/vic/den/blob/main/modules/context/user.nix) for all the details.
130130+131131+## `den.provides.mutual-provider`
132132+133133+An alternative to bidirectionality is [`den.provides.mutual-provider`](https://github.com/vic/den/blob/main/modules/aspects/provides/mutual-provider.nix).
134134+135135+This battery is more explicit, since it requires an explicit `.provides.` relationship between users and hosts.
136136+137137+```nix
138138+# Host provides to a particular user
139139+den.aspects.igloo.provides.tux = {
140140+ hjem = ...;
141141+};
142142+143143+# User provides to a particular host
144144+den.aspects.tux.provides.igloo = {
145145+ nixos = ...;
146146+};
147147+```
148148+149149+To enable it for both users and hosts, include at default:
150150+151151+```nix
152152+den.default.includes = [ den._.mutual-provider ];
153153+```
···66import { Aside } from '@astrojs/starlight/components';
7788<Aside title="Source" icon="github">
99-[`modules/aspects/`](https://github.com/vic/den/tree/main/modules/aspects) · Tests: [`features/user-host-bidirectional-config.nix`](https://github.com/vic/den/blob/main/templates/ci/modules/features/user-host-bidirectional-config.nix)
99+[`github:vic/flake-aspects`](https://github.com/vic/flake-aspects) ·
1010+[`aspects definition`](https://github.com/vic/den/tree/main/modules/aspects/definition.nix) ·
1111+[`example aspects`](https://github.com/vic/den/tree/main/modules/aspects/provides) ·
1012</Aside>
11131214# Den created aspects.
1313-1414-An aspect is an attrset with per-class modules.
15151616<Aside icon="nix" title="Nix Classes">
17171818-An flake-aspect (used by Den) is just an attrset that contains modules of different Nix `class`.
1818+A Dendritic aspect is an attrset that contains modules of different Nix `class`.
19192020```nix
2121-gaming = { nixos = ...; homeManager = ...; hjem = ...; }
2121+gaming = { nixos = ...; homeManager = ...; hjem = ...; darwin = ...; }
2222```
23232424-Such an attrset is said to be [**Dendritic**](https://dendrix.oeiuwq.com/Dendritic.html) because it allows configuring a single cross-cutting concern (`gaming`) over different Nix classes.
2424+Such an attrset is said to be [**Dendritic**](https://dendrix.oeiuwq.com/Dendritic.html) because it allows configuring a single cross-cutting concern (`gaming`) over different configuration domains.
25252626Nix classes are NOT an artificial concept created by Den. There are [many nix classes in the wild](https://github.com/search?q=language%3ANix+%28%22+class+%3D+%22+AND+%22evalModules%22%29&type=code), the most common are [`nixos`](https://github.com/search?q=repo%3ANixOS%2Fnixpkgs+%22class+%3D+%5C%22nixos%5C%22%22&type=code), [`darwin`](https://github.com/nix-darwin/nix-darwin/blob/da529ac9e46f25ed5616fd634079a5f3c579135f/eval-config.nix#L81), [`homeManager`](https://github.com/nix-community/home-manager/blob/5be5d8245cbc7bc0c09fbb5f38f23f223c543f85/nixos/common.nix#L27).
2727</Aside>
···58585959## Aspect Basics
60606161-In addition to class modules, an aspect has an `includes` list referencing other aspects, and `provides` for sub-aspects in same category:
6161+In addition to per-class modules, an aspect has an `includes` list referencing other aspects, and `provides` for sub-aspects in same category:
62626363```nix
6464{ den, ... }: {
···72727373 # Dependencies
7474 includes = [
7575- den.aspects.dev-tools
7675 den.provides.primary-user
7676+ den.aspects.gaming.provides.emulation
7777 ];
7878-7979- # Named sub-aspects
8080- provides.gpu = { host, ... }:
8181- lib.optionalAttrs (host ? gpu && host.gpu == "nvidia") {
8282- nixos.hardware.nvidia.enable = true;
8383- };
8478 };
7979+8080+ den.aspects.gaming = {
8181+ darwin = ...;
8282+ nixos = ...;
8383+ provides.emulation = {
8484+ darwin = ...;
8585+ nixos = ...;
8686+ };
8787+ }
8588}
8689```
87908891## Owned Configs
89929093Attributes named after a Nix class (`nixos`, `darwin`, `homeManager`, or any
9191-custom class) are **owned configs**. They can be:
9494+custom class) are referred by this documentation as **owned configs**.
92959393-- **Attrsets**: merged directly into the class configuration.
9494-- **Functions**: called with the class's module arguments (`{ config, pkgs, lib, ... }`).
9696+They are just a regular Nix module:
9797+9898+- **Attrset Module**: plain and simple configuration.
9999+- **Function Module**: taking module arguments (`{ config, pkgs, lib, ... }: { }`).
9510096101```nix
97102# Attrset form
···105110106111## Includes
107112108108-`includes` is a list of aspects, attrsets, or parametric functions:
113113+`includes` is a list of aspects used to declare dependencies between aspects.
114114+115115+Unlike other Nix libraries that use stringly-typed "tags" to define requirements,
116116+Den uses real aspect references, these can be type-checked by the Nix module system.
109117110118```nix
111119den.aspects.workstation.includes = [
112120 # Reference another aspect (its full DAG is included)
113121 den.aspects.dev-tools
122122+];
123123+```
114124115115- # Static attrset (always applied)
116116- { nixos.programs.vim.enable = true; }
125125+It is important to note the three kinds of values that Den distinguishes
126126+as part of an includes list:
117127118118- # Parametric function (applied when context shape matches)
119119- ({ host, ... }: { nixos.networking.hostName = host.hostName; })
128128+1. Static (plain attribute set): `{ nixos.foo = ...; }`
129129+2. Static (flake-aspects' leaf): `{class, aspect-chain}: { ${class}.foo = ...; }`
130130+3. Parametric (any other function): `{ host, user }: { ${host.class}.foo = ...; }`
120131121121- # Battery
122122- (den.provides.user-shell "fish")
123123-];
124124-```
132132+`(1)` and `(2)` are termed *static aspects* and are the terminal leafs of flake-aspects DAG.
133133+`(1)` provides configuration unconditionally, and `(2)` gets access to the `class` that is
134134+being resolved and an `aspect-chain` that lead to the current aspect (most recent last).
135135+136136+137137+`(3)` is a more interesting kind of aspect that is used by Den to pass host/user defitions
138138+into these functions, so they can inspect the host features and provide
139139+configuration accordingly.
140140+125141126142## Provides
127143···169185Parametric functions in `den.default.includes` are evaluated at every context
170186stage. Use `den.lib.take.exactly` if a function should only run in specific contexts.
171187</Aside>
172172-173173-## Bidirectional Flow
174174-175175-Users configure their hosts. Hosts configure their users. Aspects flow
176176-in both directions:
177177-178178-```nix
179179-# User aspect contributes to host's NixOS config
180180-den.aspects.alice = {
181181- nixos.users.users.alice.extraGroups = [ "docker" ];
182182- homeManager = { pkgs, ... }: { home.packages = [ pkgs.htop ]; };
183183-};
184184-185185-# Host aspect contributes to all its users' home config
186186-den.aspects.laptop = {
187187- homeManager.programs.ssh.enable = true;
188188- nixos.services.openssh.enable = true;
189189-};
190190-```
+13-35
docs/src/content/docs/guides/declare-hosts.mdx
···778899<Aside title="Source" icon="github">
1010-[`modules/_types.nix`](https://github.com/vic/den/blob/main/modules/_types.nix)
1010+[`types`](https://github.com/vic/den/blob/main/nix/types.nix) -
1111+[`schema`](https://github.com/vic/den/blob/main/modules/options.nix) -
1212+[`examples/microvm`](https://github.com/vic/den/blob/main/templates/microvm/modules/microvm-integration.nix)
1113</Aside>
12141315···2830```
29313032Each host entry produces a configuration in `flake.nixosConfigurations` or
3131-`flake.darwinConfigurations` depending on its `class` (auto-detected from system).
3333+`flake.darwinConfigurations` depending on its `class` (auto-detected from the host platform).
32343335## Host Schema
34363537<Aside title="Important" icon="nix">
3636-Be sure to read about the entity [Schemas](/reference/schema/), each of `host`/`user`/`home` has a corresponding `den.schema.*` module for meta-configuration. Which can later be used in aspects that read from `host`. These schemas are the meta-data equivalent of what Dendritic flake-parts users do with flake-level options.
3838+Be sure to read about the entity [Schemas](/reference/schema/), each of `host`/`user`/`home` has a corresponding `den.schema.*` module for meta-configuration of entity features. Which can later be used in aspects that read from `host`. These schemas are the meta-data equivalent of what Dendritic flake-parts users do with flake-level options.
3739</Aside>
38403941Hosts have these options (all with sensible defaults):
···53555456## User Declaration
55575656-Users are declared under a host:
5858+Users are declared as part of a host:
57595860```nix
5961den.hosts.x86_64-linux.laptop = {
···102104103105## Base Modules
104106105105-`den.schema.{host,user,home,conf}` provides shared configuration applied to all entities of each kind.
107107+`den.schema.{host,user,home}` provides shared configuration applied to all entities of each kind.
106108107107-Some batteries also extend base modules [see `hjem-os.nix`](https://github.com/vic/den/blob/main/modules/aspects/provides/hjem/hjem-os.nix#L50) which defines `hjem.module` option.
109109+Some batteries also extend base modules [see `home-env.nix`](https://github.com/vic/den/blob/main/nix/home-env.nix)
110110+that is used by home-manager/hjem/maid to extend the Host schema with options like `hjem.module`/`home-manager.module`.
111111+112112+Another more advanced examples is our [templates/microvm](https://github.com/vic/den/tree/main/templates/microvm/modules/microvm-integration.nix)
113113+that adds options related to running virtualized OS.
108114109115```nix
110116{
111111- # Can be used to config each host
117117+ # Can be used to specify features of all host
112118 den.schema.host.home-manager.enable = true;
113119114120 # Can be used to add schema options with defaults
···122128 };
123129}
124130```
125125-126126-## Aspect Association
127127-128128-Each host and user has an `aspect` name (defaults to the config `name`).
129129-130130-Den **automatically* creates a **parametric** aspect for each host, user and home using their specified classes.
131131-132132-```nix
133133-{
134134- den.hosts.x86_64-linux.laptop = {
135135- class = "nixos"; # default derived from x86_64-linux
136136- users.alice = { classes = [ "homeManager" "hjem" ]; };
137137- };
138138-139139- # Den creates the following aspects:
140140-141141- den.aspects.laptop = parametric {
142142- nixos = {};
143143- };
144144-145145- den.aspects.alice = parametric {
146146- homeManager = {};
147147- hjem = {};
148148- };
149149-}
150150-```
151151-152152-Later, when the context pipeline looks up `den.aspects.${aspect}` to find the configuration for each entity.
153131154132## Freeform Schema
155133