NixOS batteries
yen.lib.batteries.nixos holds the NixOS rules that most fleets write. NixOS is the domain of these batteries:
they set NixOS options. They still name no yen kind, sink or aspect.
You write yen.kinds.host, the sinks and every aspect, in your own words. This is a fleet of hosts that hold users:
# Your words: hosts build NixOS and hold users. The batteries fill values; every key here is yours.{ yen, inputs, ...}:let nixos = yen.lib.batteries.nixos;in{ yen.kinds.fleet = { spawns.host = _: yen.host; sinks.out.reduce = yen.lib.reduce.merge; }; yen.kinds.host = { spawns.user = yen.lib.pointing "hosts" yen.user; sinks.nixos.out = nixos.system { # (1) from = yen.kinds.host.sinks.nixos; inherit (inputs) nixpkgs; }; # package names from any feature or user; one module per host allows them sinks.unfree.nixos = nixos.unfree { from = yen.kinds.host.sinks.unfree; }; # (2) sinks.insecure.nixos = nixos.insecure { from = yen.kinds.host.sinks.insecure; }; # (3) }; yen.kinds.user.fields = { hosts = yen.lib.listOf yen.kinds.host; shell = yen.lib.opt (yen.lib.enum [ "bash" "fish" "zsh" ]) "bash"; }; yen.kinds.host.fields.autologin = yen.lib.maybe yen.kinds.user;
yen.fleet.main = { }; yen.flake = { out, ... }: out;}(1)issystem, theoutkey of the sink that collects NixOS modules. It buildsnixosConfigurations.<host>.(2)and(3)are the NixOS keys of two sinks of package names. One module per host allows or marks them. Any aspect can give names to these sinks.
These are the data of the fleet and the aspect that gives the NixOS base to the hosts:
{ yen, ... }:{ yen.host.desk = { addr = "10.0.0.2"; sshKey = "ssh-ed25519 AAAAdesk"; autologin = yen.user.ana; }; yen.host.nas = { addr = "10.0.0.1"; sshKey = "ssh-ed25519 AAAAnas"; }; yen.user.ana = { hosts = [ yen.host.desk ]; shell = "fish"; }; yen.user.bob.hosts = [ yen.host.desk ]; yen.aspects.desk = { at = [ yen.host.desk yen.host.nas ]; nixos = { boot.loader.grub.device = "nodev"; fileSystems."/" = { device = "/dev/sda1"; fsType = "ext4"; }; nixpkgs.hostPlatform = "x86_64-linux"; system.stateVersion = "25.05"; }; }; yen.aspects.gaming = { at = yen.host.desk; unfree = [ "steam" "steam-unwrapped" ]; nixos.programs.steam.enable = true; }; yen.aspects.bob-tools = { at = yen.user.bob; unfree = [ "vscode" ]; insecure = [ "olm-3.2.16" ]; };}The aspects that set the host name, the users and their shells:
# Den's hostname, define-user and primary-user, as values of aspects you name.{ yen, ... }:let nixos = yen.lib.batteries.nixos;in{ yen.aspects.hostname = { at = yen.kinds.host; nixos = nixos.hostname; # (1) }; yen.aspects.accounts = { at = yen.kinds.user; # a user has no `nixos` sink: its account goes into each of its hosts nixos = nixos.define-user; # (2) }; yen.aspects.admin = { at = yen.user.ana; nixos = nixos.primary-user { }; # (3) }; # Den's user-shell and tty-autologin: the battery reads the field you name yen.aspects.shells = { at = yen.kinds.user; nixos = nixos.user-shell { shell = u: u.shell; }; # (4) }; yen.aspects.autologin = { at = yen.kinds.host; nixos = nixos.autologin { user = h: h.autologin; }; # (5) };}Each battery is the value of the nixos key of an aspect. (1) sets the host name from the name of the scope.
(2) defines a user’s account. The kind user has no nixos sink, so the account goes into each of its hosts.
(3) makes ana the primary user. (4) reads the shell field that you name. At (5), user reads a ref or a name.
Ports, peers and builders:
# Ports, peers and builders: sinks you name, NixOS values from batteries.{ yen, lib, ... }:let nixos = yen.lib.batteries.nixos; str = yen.lib.str;in{ yen.kinds.host = { fields = { addr = str; sshKey = str; }; sinks.ports.nixos = nixos.firewall { from = yen.kinds.host.sinks.ports; }; # (1) }; # every host gives a peer record and a builder record to the fleet; each host reads the others' yen.kinds.fleet.sinks = { peers.reduce = yen.lib.reduce.elements; builders.reduce = yen.lib.reduce.elements; }; yen.aspects.peer = { at = yen.kinds.host; peers = { host, ... }: { inherit (host) name addr; key = host.sshKey; }; builders = { host, ... }: { hostName = host.addr; system = "x86_64-linux"; maxJobs = 4; }; nixos = nixos.hosts { from = yen.kinds.fleet.sinks.peers; }; # (2) }; yen.aspects.builds = { at = yen.kinds.host; nixos = nixos.remote-builders { from = yen.kinds.fleet.sinks.builders; }; # (3) }; yen.aspects.ssh = { at = yen.kinds.host; ports = 22; nixos.services.openssh.enable = true; }; yen.aspects.dns = { at = yen.host.nas; ports = { tcp = [ 53 ]; udp = [ 53 ]; }; };}(1) opens the ports that aspects give: 22, or { tcp; udp; }. At (2) and (3), every host gives a record to the fleet, and each
host reads the records of the other machines: /etc/hosts and known_hosts, and nix.buildMachines.
Paths that survive a reboot, and hardware:
# Paths that survive a reboot, from any feature (impermanence), and hardware from a facter report where one exists.{ yen, inputs, ... }:let nixos = yen.lib.batteries.nixos;in{ yen.kinds.host.sinks.persist.nixos = nixos.persist { # (1) from = yen.kinds.host.sinks.persist; hideMounts = true; }; yen.aspects.impermanence = { at = yen.kinds.host; nixos.imports = [ inputs.impermanence.nixosModules.impermanence ]; }; yen.aspects.ssh-state = { at = yen.kinds.host; persist.files = [ "/etc/ssh/ssh_host_ed25519_key" ]; }; yen.aspects.ana-state = { at = yen.user.ana; # from a user: up to each of her hosts persist = "/home/ana/.local/share"; }; # facter: only hosts with a report under modules/<host>/ yen.aspects.facter = { at = s: builtins.pathExists (./. + "/${s.name}/facter.json"); nixos = nixos.facter { # (2) report = h: ./. + "/${h.name}/facter.json"; module = inputs.nixos-facter-modules.nixosModules.facter; }; };}(1) collects paths from any aspect, for impermanence. A path from a user goes up to each of her hosts. (2) adds a
facter module only at hosts that have a report.
The batteries
Section titled “The batteries”| Battery | Value of | Den |
|---|---|---|
system { from; nixpkgs; } |
the out key of the sink that collects NixOS modules |
the den.hosts schema |
hostname |
a NixOS key of an aspect | den.batteries.hostname |
define-user |
a NixOS key of an aspect | den.batteries.define-user |
primary-user { user ? ; } |
a NixOS key; user reads a ref or a name, by default the scope |
den.batteries.primary-user |
unfree { from; } |
the NixOS key of a sink of package names | den.batteries.unfree |
insecure { from; } |
the NixOS key of a sink of package names | den.batteries.insecure |
user-shell { shell; } |
a NixOS key; shell reads the scope |
den.batteries.user-shell |
autologin { user; } |
a NixOS key; user reads a ref or a name |
den.batteries.tty-autologin |
firewall { from; } |
the NixOS key of a sink of ports (22 or { tcp; udp; }) |
quirks firewall |
hosts { from; } |
a NixOS key; /etc/hosts and known_hosts of the other machines (mesh) |
sini hostsfile |
remote-builders { from; systemOf; } |
a NixOS key; nix.buildMachines of the other machines (mesh) |
sini remote-build-client |
persist { from; root; hideMounts; } |
the NixOS key of a sink of paths (impermanence) | quirks persist |
facter { report; module; } |
a NixOS key, at hosts that have a report | den.aspects.facter |
mesh is a generic battery. Each battery is a few lines. The pages Every Den battery and
Unfree packages from many files write the same rules by hand. When a battery does not fit your
fleet, copy those lines and change them: see Write your own.