{ lib, config, ... }: let inherit (lib) concatLists flip mapAttrsToList mkDefault mdDoc mkIf mkOption types ; cfg = config.users.deterministicIds; in { options = { users.deterministicIds = mkOption { default = {}; description = mdDoc '' Maps a user or group name to its expected uid/gid values. If a user/group is used on the system without specifying a uid/gid, this module will assign the corresponding ids defined here, or show an error if the definition is missing. ''; type = types.attrsOf (types.submodule { options = { uid = mkOption { type = types.nullOr types.int; default = null; description = mdDoc "The uid to assign if it is missing in `users.users.`."; }; gid = mkOption { type = types.nullOr types.int; default = null; description = mdDoc "The gid to assign if it is missing in `users.groups.`."; }; }; }); }; users.users = mkOption { type = types.attrsOf (types.submodule ({name, ...}: { config.uid = let deterministicUid = cfg.${name}.uid or null; in mkIf (deterministicUid != null) (mkDefault deterministicUid); })); }; users.groups = mkOption { type = types.attrsOf (types.submodule ({name, ...}: { config.gid = let deterministicGid = cfg.${name}.gid or null; in mkIf (deterministicGid != null) (mkDefault deterministicGid); })); }; }; config = { assertions = concatLists (flip mapAttrsToList config.users.users (name: user: [ { assertion = user.uid != null; message = "non-deterministic uid detected for '${name}', please assign one via `users.deterministicIds`"; } { assertion = !user.autoSubUidGidRange; message = "non-deterministic subUids/subGids detected for: ${name}"; } ])) ++ flip mapAttrsToList config.users.groups (name: group: { assertion = group.gid != null; message = "non-deterministic gid detected for '${name}', please assign one via `users.deterministicIds`"; }); }; }