Skip to content

Unfree packages from many files

nixpkgs.config.allowUnfree = true allows every unfree package. To allow only some, nixpkgs has allowUnfreePredicate. It is a function, so the module system cannot merge it: when two modules set it, they conflict (nixpkgs#55674, discourse). The usual fix declares a list option and sets the predicate once from it. Den ships the same fix as den.batteries.unfree, its most used battery (Every Den battery).

In yen the list option is a sink. This file declares it on the host kind:

modules/unfree.nix
# allowUnfreePredicate is a function: two modules that set it conflict. A sink collects names; one predicate reads them.
{ lib, ... }:
{
yen.kinds.host.sinks.unfree = {
# (1)
nixos = # (2)
{ unfree, ... }:
{
nixpkgs.config.allowUnfreePredicate = pkg: lib.elem (lib.getName pkg) (lib.flatten unfree);
};
# the list per host, as a flake output: review it before a deploy
out = # (3)
{ host, unfree, ... }:
{
unfree.${host.name} = lib.sort lib.lessThan (lib.flatten unfree);
};
};
}

(1) is the sink. (2) is its nixos key: one predicate that reads every name the sink collected. (3) is its out key: the list of each host becomes a flake output, so you can see it.

A feature gives the names that it needs, next to the packages. A user can give names too: they go up to each host of the user.

modules/features.nix
# Each feature names the unfree packages it needs, next to the packages. No file knows the whole list.
{ yen, ... }:
{
yen.aspects.gaming = {
at = yen.host.desk;
unfree = [
# (1)
"steam"
"steam-unwrapped"
];
nixos.programs.steam.enable = true;
};
# from a user: a user has no `unfree` sink, so the names go up to each of its hosts
yen.aspects.ana-editor = {
at = yen.user.ana;
unfree = [ "vscode" ]; # (2)
nixos =
{ pkgs, ... }:
{
users.users.ana = {
isNormalUser = true;
packages = [ pkgs.vscode ];
};
};
};
}

(1) gives the names of the gaming aspect. (2) gives the names from a user. The server host has no names, so its sink gives nothing: nixpkgs refuses every unfree package there, as by default. The flake output shows the list of each host:

result
{
desk = [
"steam"
"steam-unwrapped"
"vscode"
];
}
modules/fleet.nix
# Hosts build NixOS and hold their users.
{
yen,
inputs,
...
}:
{
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 =
{ host, nixos, ... }:
{
nixosConfigurations.${host.name} = inputs.nixpkgs.lib.nixosSystem { modules = nixos; };
};
};
yen.kinds.user.fields.hosts = yen.lib.listOf yen.kinds.host;
yen.fleet.main = { };
yen.flake = { out, ... }: out;
yen.host.desk = { };
yen.host.server = { };
yen.user.ana.hosts = [ yen.host.desk ];
yen.aspects.machine = {
at = yen.kinds.host;
nixos =
{ host, ... }:
{
networking.hostName = host.name;
boot.loader.grub.device = "nodev";
fileSystems."/" = {
device = "/dev/sda1";
fsType = "ext4";
};
nixpkgs.hostPlatform = "x86_64-linux";
system.stateVersion = "25.05";
};
};
}
ContributeCommunitySponsor