{ config, lib, flake-parts-lib, inputs, den, ... }: let inherit (lib) mkOption types; cfg = config.secrets; in { options.secrets = { masterKeys = mkOption { type = types.listOf types.str; default = [ ]; description = "A list of master keys for encrypting secrets."; }; secretsDir = mkOption { type = types.str; default = null; description = "Path to the directory containing secrets relative to flake root."; }; formatter = mkOption { type = lib.types.functionTo lib.types.unspecified; default = pkgs: pkgs.prettier; description = "The formatter function to use for formatting sops file"; }; nixosModule = mkOption { type = types.deferredModule; readOnly = true; description = "NixOS module configuration for managing sops secrets."; }; }; config = let secrets = builtins.fromJSON (builtins.readFile "${inputs.self}/${cfg.secretsDir}/secrets.json"); all_keys = lib.flatten (lib.concatAttrValues per_host_keys); per_host_keys = lib.mergeAttrsList ( lib.flatten ( map ( x: builtins.mapAttrs ( name: value: let v = value.sopsPublic or [ ]; type = builtins.typeOf v; vv = if type == "string" then [ v ] else if type == "list" then v else throw "Unexpected type ${type} for sopsPublic in host ${name}"; in vv ) x ) (builtins.attrValues den.hosts) ) ); secret_map = lib.mapAttrs ( name: value: let keys = lib.unique ( lib.flatten (map (k: per_host_keys.${k}) value.hosts) ++ (lib.optionals value.global all_keys) ++ cfg.masterKeys ); in { inherit (value) format neededForUsers hosts global ; inherit keys; } ) secrets; rules = lib.mapAttrsToList (name: value: { path_regex = "${cfg.secretsDir}/${name}$"; key_groups = [ { age = value.keys; } ]; }) secret_map; sops_secrets_map = lib.concatMapAttrs ( name: value: let hasHost = (lib.elem "wawa" value.hosts) || value.global; in if hasHost then { ${name} = { inherit (value) format neededForUsers; sopsFile = inputs.self + "/${cfg.secretsDir}/${name}"; }; } else { } ) secret_map; in { secrets.nixosModule = { ... }: { config.sops.secrets = sops_secrets_map; }; perSystem = { pkgs, self', ... }: let formatted = let unformatted = (pkgs.formats.yaml { }).generate ".sops.yaml" { creation_rules = rules; }; in pkgs.stdenvNoCC.mkDerivation { name = ".sops-yaml-formatted"; src = unformatted; phases = [ "format" ]; format = '' cp $src .sops.yaml chmod +w .sops.yaml ${lib.getExe (cfg.formatter pkgs)} .sops.yaml cp .sops.yaml $out ''; }; in { packages.write-sops-config = pkgs.writeShellApplication { name = "write-sops-config"; text = '' cp ${formatted} .sops.yaml find ${cfg.secretsDir} -type f ! -name "secrets.json" -exec ${pkgs.lib.getExe pkgs.sops} updatekeys -y {} \; ''; }; checks.check-sops-config = pkgs.runCommand "check-sops-config" { nativeBuildInputs = [ pkgs.diffutils ]; } '' set -e diff -u ${inputs.self}/.sops.yaml ${formatted} touch $out ''; }; }; }