Every Den battery
A Den battery is an aspect that Den ships. You include it, sometimes with an argument:
(den.batteries.user-shell "fish"). In yen there is nothing to ship. The argument becomes a typed field of
the host or the user, and the battery is a short aspect whose at says where it applies.
The table lists every battery in Den’s modules/aspects/batteries/. The count is how often each one appears in
the 20 most starred public Den configurations on GitHub (October 2026).
| Den battery | Uses | yen |
|---|---|---|
unfree [ … ] |
41 | nixos.unfree, or by hand: Unfree packages from many files |
primary-user |
30 | nixos.primary-user, or by hand: From Den |
define-user |
27 | nixos.define-user, or by hand: From Den |
hostname |
25 | nixos.hostname, or by hand below |
forward |
16 | a sink whose key gives to another sink: Custom classes |
user-shell "fish" |
11 | a shell field and one aspect, below |
inputs', self' |
21 | inputs is an arg of every yen module; per system: inputs.x.packages.${pkgs.stdenv.hostPlatform.system} |
flake-scope |
– | nothing: yen, lib and inputs are args of every yen module |
host-aspects |
9 | an aspect at the users within the host that opt in: includes and provides |
mutual-provider |
6 | nothing: in Den it is inert; in yen within routes both ways |
tty-autologin "ana", vm-autologin |
7 | a host field that names a user, below |
insecure [ … ] |
5 | nixos.insecure, or by hand below: nixpkgs.config does not merge lists |
import-tree ./dir |
2 | the yen template already loads modules/ with import-tree; plain NixOS files go in a nixos.imports |
os class (os-class) |
– | an os sink that forwards to nixos and darwin: nix-darwin machines |
user class (os-user) |
– | an account sink on the user, below |
home-manager |
– | an aspect where a host has users: From Den |
hjem, maid |
– | a user sink, chosen by a classes field: den/custom-classes.nix |
wsl |
– | a host field wsl and an aspect whose at reads it, as in Many hosts, one base |
Parameters are fields
Section titled “Parameters are fields”user-shell "fish" and tty-autologin "ana" take an argument. In yen the argument is a typed
field on the instance. This file declares the Den schema, with both fields.
# Den's schema, declared: hosts build NixOS and hold users. Battery parameters are typed fields.{ yen, inputs, ...}:let inherit (yen.lib) opt maybe;in{ yen.kinds.fleet = { spawns.host = _: yen.host; sinks.out.reduce = yen.lib.reduce.merge; }; yen.kinds.host = { fields.autologin = maybe yen.kinds.user; # (1) den.batteries.tty-autologin "ana" spawns.user = yen.lib.pointing "hosts" yen.user; sinks.nixos.out = { host, nixos, ... }: { nixosConfigurations.${host.name} = inputs.nixpkgs.lib.nixosSystem { modules = nixos; }; }; }; yen.kinds.user.fields = { hosts = yen.lib.listOf yen.kinds.host; shell = opt (yen.lib.enum [ "bash" "zsh" "fish" ]) "bash"; # (2) den.batteries.user-shell "fish" };
yen.fleet.main = { }; yen.flake = { out, ... }: out;
yen.host.box.autologin = yen.user.ana; yen.user.ana = { hosts = [ yen.host.box ]; shell = "fish"; }; yen.user.bob.hosts = [ yen.host.box ]; yen.aspects.box = { at = yen.host.box; nixos = { boot.loader.grub.device = "nodev"; fileSystems."/" = { device = "/dev/sda1"; fsType = "ext4"; }; nixpkgs.hostPlatform = "x86_64-linux"; system.stateVersion = "25.05"; }; };}(1) is the autologin field of a host. Its type is a user or nothing, so autologin must be a user
that exists. (2) is the shell field of a user. Its type is one of three names, and the default is bash.
A typo such as shell = "fsh" is an error at yen.user.ana.shell.
Each battery below is an aspect. Its at says where it applies, and its nixos key
gives a NixOS module to the sink of that name.
# Den batteries as yen aspects. A battery's `includes` site becomes the aspect's `at`; its argument becomes a field.{ yen, lib, ... }:{ # den.batteries.hostname yen.aspects.hostname = { at = yen.kinds.host; nixos = { host, ... }: { networking.hostName = host.name; }; };
# den.batteries.user-shell: the shell is the user's field yen.aspects.user-shell = { at = yen.kinds.user; nixos = { user, pkgs, ... }: { programs.${user.shell}.enable = lib.mkIf (user.shell != "bash") true; users.users.${user.name}.shell = pkgs.${user.shell}; }; };
# den.batteries.tty-autologin: on the hosts that name a user yen.aspects.tty-autologin = { at = s: s.autologin or null != null; nixos = { host, ... }: { services.getty.autologinUser = host.autologin.name; }; };
# den.batteries.define-user, short yen.aspects.define-user = { at = yen.kinds.user; nixos = { user, ... }: { users.users.${user.name}.isNormalUser = true; }; };
# Den's `user` class (os-user): a user sink that writes users.users.<name>. # yen calls it `account`, because `user` is the arg that holds the user itself. # A sink with no elements gives nothing, so keep defaults out of it: they go in define-user. yen.kinds.user.sinks.account.nixos = { user, account, ... }: { users.users.${user.name} = lib.mkMerge account; }; yen.aspects.ana-tools = { at = yen.user.ana; account = { description = "Ana"; extraGroups = [ "wheel" ]; }; };}Insecure packages need a sink too
Section titled “Insecure packages need a sink too”permittedInsecurePackages is a list, but it is in nixpkgs.config, and nixpkgs.config does not merge lists.
When two NixOS modules set it, only one list survives, with no error. We wrote this page first without the
sink, on NixOS 25.05, and bob’s entry was lost. The fix is the same as for unfree
(Unfree packages from many files): a sink of names, and one module that sets the option.
# den.batteries.insecure: nixpkgs.config does not merge lists. When two modules set permittedInsecurePackages,# only one list survives. A sink collects the names, and one module sets the option.{ yen, lib, ... }:{ yen.kinds.host.sinks.insecure.nixos = { insecure, ... }: { nixpkgs.config.permittedInsecurePackages = lib.flatten insecure; };
yen.aspects.matrix-client = { at = yen.host.box; insecure = [ "olm-3.2.16" ]; }; yen.aspects.old-tool = { at = yen.user.bob; # from a user: the names go up to each of bob's hosts insecure = [ "python-2.7.18.8" ]; };}