feat(secrets): Base names on encrypted contents

This is useful, for example, to make systemd services restart without
fiddling with restart triggers. In sops we achieved this by setting the
attribute name using a function - this isn't possible with agenix-rekey
because it needs to evaluate secrets (including their attribute names)
when the files don't yet exist

Despite this, we can still set the "name" - which is used only when
rekeying and deploying the secret - and manually handle attribute names

Change-Id: Ia49c7fe9eb55341f433cbb7c49935584b48518fe
Reviewed-on: https://git.clicks.codes/c/Infra/NixFiles/+/806
Tested-by: Skyler Grey <minion@clicks.codes>
Reviewed-by: Skyler Grey <minion@clicks.codes>
diff --git a/modules/nixos/clicks/security/secrets/instability/default.nix b/modules/nixos/clicks/security/secrets/instability/default.nix
new file mode 100644
index 0000000..f1362a4
--- /dev/null
+++ b/modules/nixos/clicks/security/secrets/instability/default.nix
@@ -0,0 +1,51 @@
+{ config, lib, ... }: {
+  options.clicks.security.secrets.instability.enable = lib.mkOption {
+    description = ''
+      Enable changing secret names using instability by default
+
+      This is useful, for example, to make systemd services restart without
+      fiddling with restart triggers, but could be detrimental to services like
+      nginx which can reload with zero downtime (but won't necessarily do so if
+      you swap secret files from under them)
+
+      This also works with agenix-rekey, and if you're using that then the
+      secret name will be based on the rekeyFile
+      '';
+    type = lib.types.bool;
+    default = config.clicks.security.secrets.enable;
+  };
+
+  options.age = {
+    # Extend age.secrets with the ability to have an unstable name
+    secrets = lib.mkOption {
+      type = lib.types.attrsOf (lib.types.submodule (submodule: {
+        options = {
+          unstableName = lib.mkOption {
+            type = lib.types.bool;
+            default = config.clicks.security.secrets.instability.enable;
+            example = true;
+            description = ''
+              Whether the name of this secret should be based on the (encrypted)
+              contents of its file
+
+              This is useful, for example, to make systemd services restart
+              without fiddling with restart triggers, but could be detrimental
+              to services like nginx which can reload with zero downtime (but
+              won't necessarily do so if you swap secret files from under them)
+
+              This also works with agenix-rekey, and if you're using that then
+              the secret name will be based on the rekeyFile
+            '';
+          };
+        };
+        config = {
+          # Calculate the name as the sha256 hash of the rekeyFile or file... whichever happens to exist for this secret
+          name = let
+            dependency = submodule.config.rekeyFile or submodule.config.file;
+            hash = builtins.hashFile "sha256" dependency;
+          in lib.mkIf submodule.config.unstableName hash;
+        };
+      }));
+    };
+  };
+}