diff --git a/modules/system/fieldseeker-sync.nix b/modules/system/fieldseeker-sync.nix index 9dcdf40..beaa540 100644 --- a/modules/system/fieldseeker-sync.nix +++ b/modules/system/fieldseeker-sync.nix @@ -1,246 +1,195 @@ { config, inputs, lib, pkgs, ... }: with lib; let + cfg = config.myModules.fieldseeker-sync; fieldseeker-sync-pkg = inputs.fieldseeker-sync.packages.x86_64-linux.default; + mkMergeTopLevel = names: attrs: + lib.getAttrs names ( + lib.mapAttrs (_k: v: lib.mkMerge v) (lib.foldAttrs (n: a: [ n ] ++ a) [ ] attrs) + ); in { - options.myModules.fieldseeker-sync.enable = mkEnableOption "custom fieldseeker-sync configuration"; + options.myModules.fieldseeker-sync.deployments = mkOption { + type = types.listOf (types.submodule { + options = { + customer = mkOption { + type = types.str; + description = "Name of the customer"; + }; + + dataDirectory = mkOption { + type = types.path; + description = "Directory for the data files"; + }; + + port = mkOption { + type = types.int; + description = "Port for the service"; + }; + + secretsPath = mkOption { + type = types.path; + description = "Path to the secrets file"; + }; + + subdomain = mkOption { + type = types.str; + description = "Subdomain for the customer"; + }; - config = mkIf config.myModules.fieldseeker-sync.enable { - environment.systemPackages = [ - fieldseeker-sync-pkg - pkgs.ffmpeg - ]; - services.caddy.virtualHosts."deltamvcd.nidus.cloud".extraConfig = '' - reverse_proxy http://127.0.0.1:3000 - ''; - services.caddy.virtualHosts."gleipnir.nidus.cloud".extraConfig = '' - reverse_proxy http://127.0.0.1:3001 - ''; - services.postgresql = { - enable = true; - ensureDatabases = [ "fieldseeker-sync" ]; - ensureUsers = [{ - ensureClauses.login = true; - ensureDBOwnership = true; - name = "fieldseeker-sync"; - }]; - }; - services.restic.backups.deltamvcd-db = { - # We can use this due to overridding restic with unstable - command = [ - "${lib.getExe pkgs.sudo}" - "-u postgres" - "${pkgs.postgresql}/bin/pg_dump fieldseeker-sync" - ]; - environmentFile = "/var/run/secrets/restic-env"; - extraBackupArgs = [ - "--tag database" - ]; - initialize = true; - passwordFile = "/var/run/secrets/restic-password"; - pruneOpts = [ - "--keep-daily 14" - "--keep-weekly 4" - "--keep-monthly 2" - "--group-by tags" - ]; - repository = "s3:s3.us-west-004.backblazeb2.com/gleipnir-backup-deltamvcd/database"; - }; - services.restic.backups.deltamvcd-files = { - environmentFile = "/var/run/secrets/restic-env"; - extraBackupArgs = [ - "--tag user-files" - ]; - initialize = true; - passwordFile = "/var/run/secrets/restic-password"; - paths = [ - "/opt/fieldseeker-sync/deltamvcd" - ]; - repository = "s3:s3.us-west-004.backblazeb2.com/gleipnir-backup-deltamvcd/files"; - - }; - sops.secrets.fieldseeker-sync-env = { - format = "dotenv"; - group = "fieldseeker-sync"; - mode = "0440"; - owner = "fieldseeker-sync"; - restartUnits = ["fieldseeker-sync-webserver.service"]; - sopsFile = ../../secrets/fieldseeker-sync.env; - }; - sops.secrets.fieldseeker-sync-gleipnir-env = { - format = "dotenv"; - group = "fieldseeker-sync"; - mode = "0440"; - owner = "fieldseeker-sync"; - restartUnits = ["fieldseeker-sync-gleipnir.service"]; - sopsFile = ../../secrets/fieldseeker-sync-gleipnir.env; - }; - sops.secrets.restic-env = { - format = "yaml"; - key = "backblaze"; - group = "root"; - mode = "0440"; - owner = "root"; - #restartUnits = ["fieldseeker-sync.service"]; - sopsFile = ../../secrets/restic.yaml; - }; - sops.secrets.restic-password = { - format = "yaml"; - key = "password"; - group = "root"; - mode = "0440"; - owner = "root"; - #restartUnits = ["fieldseeker-sync.service"]; - sopsFile = ../../secrets/restic.yaml; - }; - systemd.services.fieldseeker-sync-audio-post-processor = { - after=["network.target" "network-online.target" "fieldseeker-sync-migrate.service"]; - description="FieldSeeker sync audio post processor"; - requires=["network-online.target"]; - restartIfChanged = false; - stopIfChanged = false; - serviceConfig = { - EnvironmentFile="/var/run/secrets/fieldseeker-sync-env"; - Type = "simple"; - User = "fieldseeker-sync"; - Group = "fieldseeker-sync"; - ExecStart = "${fieldseeker-sync-pkg}/bin/audio-post-processor"; - TimeoutStopSec = "5s"; - PrivateTmp = true; - WorkingDirectory = "/tmp"; }; - startAt = "*:0/15"; - wantedBy = ["multi-user.target"]; - }; - systemd.services.fieldseeker-sync-gleipnir-audio-post-processor = { - after=["network.target" "network-online.target" "fieldseeker-sync-gleipnir-migrate.service"]; - description="FieldSeeker sync audio post processor"; - requires=["network-online.target"]; - restartIfChanged = false; - stopIfChanged = false; - serviceConfig = { - EnvironmentFile="/var/run/secrets/fieldseeker-sync-gleipnir-env"; - Type = "simple"; - User = "fieldseeker-sync"; - Group = "fieldseeker-sync"; - ExecStart = "${fieldseeker-sync-pkg}/bin/audio-post-processor"; - TimeoutStopSec = "5s"; - PrivateTmp = true; - WorkingDirectory = "/tmp"; - }; - startAt = "*:0/15"; - wantedBy = ["multi-user.target"]; - }; - systemd.services.fieldseeker-sync-export = { - after=["network.target" "network-online.target" "fieldseeker-sync-migrate.service"]; - description="FieldSeeker sync periodic sync tool"; - requires=["network-online.target"]; - restartIfChanged = false; - stopIfChanged = false; - serviceConfig = { - EnvironmentFile="/var/run/secrets/fieldseeker-sync-env"; - ExecStart = "${fieldseeker-sync-pkg}/bin/full-export"; - Group = "fieldseeker-sync"; - PrivateTmp = true; - TimeoutStopSec = "5s"; - Type = "simple"; - User = "fieldseeker-sync"; - WorkingDirectory = "/tmp"; - }; - startAt = "*:0/15"; - wantedBy = ["multi-user.target"]; - }; - systemd.services.fieldseeker-sync-gleipnir-export = { - after=["network.target" "network-online.target" "fieldseeker-sync-gleipnir-migrate.service"]; - description="FieldSeeker sync periodic sync tool"; - requires=["network-online.target"]; - restartIfChanged = false; - stopIfChanged = false; - serviceConfig = { - EnvironmentFile="/var/run/secrets/fieldseeker-sync-gleipnir-env"; - ExecStart = "${fieldseeker-sync-pkg}/bin/full-export"; - Group = "fieldseeker-sync"; - PrivateTmp = true; - TimeoutStopSec = "5s"; - Type = "simple"; - User = "fieldseeker-sync"; - WorkingDirectory = "/tmp"; - }; - startAt = "*:0/15"; - wantedBy = ["multi-user.target"]; - }; - systemd.services.fieldseeker-sync-migrate = { - after=["network.target" "network-online.target"]; - description="FieldSeeker DB migrate"; - requires=["network-online.target"]; - serviceConfig = { - Environment="SENTRY_RELEASE=${inputs.fieldseeker-sync.rev}"; - EnvironmentFile="/var/run/secrets/fieldseeker-sync-env"; - Type = "oneshot"; - User = "fieldseeker-sync"; - Group = "fieldseeker-sync"; - ExecStart = "${fieldseeker-sync-pkg}/bin/migrate"; - TimeoutStopSec = "5s"; - PrivateTmp = true; - WorkingDirectory = "/tmp"; - }; - wantedBy = ["multi-user.target"]; - }; - systemd.services.fieldseeker-sync-gleipnir-migrate = { - after=["network.target" "network-online.target"]; - description="FieldSeeker Gleipnir DB migrate"; - requires=["network-online.target"]; - serviceConfig = { - Environment="SENTRY_RELEASE=${inputs.fieldseeker-sync.rev}"; - EnvironmentFile="/var/run/secrets/fieldseeker-sync-gleipnir-env"; - Type = "oneshot"; - User = "fieldseeker-sync"; - Group = "fieldseeker-sync"; - ExecStart = "${fieldseeker-sync-pkg}/bin/migrate"; - TimeoutStopSec = "5s"; - PrivateTmp = true; - WorkingDirectory = "/tmp"; - }; - wantedBy = ["multi-user.target"]; - }; - systemd.services.fieldseeker-sync-webserver = { - after=["network.target" "network-online.target" "fieldseeker-sync-migrate.service"]; - description="FieldSeeker sync"; - requires=["network-online.target"]; - serviceConfig = { - Environment="SENTRY_RELEASE=${inputs.fieldseeker-sync.rev}"; - EnvironmentFile="/var/run/secrets/fieldseeker-sync-env"; - Type = "simple"; - User = "fieldseeker-sync"; - Group = "fieldseeker-sync"; - ExecStart = "${fieldseeker-sync-pkg}/bin/webserver"; - TimeoutStopSec = "5s"; - PrivateTmp = true; - WorkingDirectory = "/tmp"; - }; - wantedBy = ["multi-user.target"]; - }; - systemd.services.fieldseeker-sync-gleipnir-webserver = { - after=["network.target" "network-online.target" "fieldseeker-sync-gleipnir-migrate.service"]; - description="FieldSeeker sync"; - requires=["network-online.target"]; - serviceConfig = { - Environment="SENTRY_RELEASE=${inputs.fieldseeker-sync.rev}"; - EnvironmentFile="/var/run/secrets/fieldseeker-sync-gleipnir-env"; - Type = "simple"; - User = "fieldseeker-sync"; - Group = "fieldseeker-sync"; - ExecStart = "${fieldseeker-sync-pkg}/bin/webserver"; - TimeoutStopSec = "5s"; - PrivateTmp = true; - WorkingDirectory = "/tmp"; - }; - wantedBy = ["multi-user.target"]; - }; - users.groups.fieldseeker-sync = {}; - users.users.fieldseeker-sync = { - group = "fieldseeker-sync"; - isSystemUser = true; - }; + }); + default = []; + description = "List of fieldseeker deployments"; }; + config = mkMergeTopLevel ["environment" "services" "sops" "systemd" "users"] ( + map ( deployment: + let + backupName = "${deployment.customer}-db"; + databaseName = "fss-${deployment.customer}"; + databaseUser = "fss-${deployment.customer}"; + environmentFile = "/var/run/secrets/fss-${deployment.customer}-env"; + fqdn = "${deployment.subdomain}.nidus.cloud"; + group = "fss-${deployment.customer}"; + user = "fss-${deployment.customer}"; + in { + environment.systemPackages = [ + fieldseeker-sync-pkg + pkgs.ffmpeg + ]; + services.caddy.virtualHosts."${deployment.subdomain}.nidus.cloud" = { + extraConfig = '' + reverse_proxy http://127.0.0.1:${toString deployment.port} + ''; + }; + services.postgresql = { + enable = true; + ensureDatabases = [databaseName]; + ensureUsers = [{ + ensureClauses.login = true; + ensureDBOwnership = true; + name = databaseUser; + }]; + }; + services.restic.backups."${backupName}-db" = { + # We can use this due to overridding restic with unstable + command = [ + "${lib.getExe pkgs.sudo}" + "-u postgres" + "${pkgs.postgresql}/bin/pg_dump ${databaseName}" + ]; + environmentFile = "/var/run/secrets/restic-env"; + extraBackupArgs = [ + "--tag database" + ]; + initialize = true; + passwordFile = "/var/run/secrets/restic-password"; + pruneOpts = [ + "--keep-daily 14" + "--keep-weekly 4" + "--keep-monthly 2" + "--group-by tags" + ]; + repository = "s3:s3.us-west-004.backblazeb2.com/gleipnir-backup-deltamvcd/database"; + }; + services.restic.backups."${backupName}-files" = { + environmentFile = "/var/run/secrets/restic-env"; + extraBackupArgs = [ + "--tag user-files" + ]; + initialize = true; + passwordFile = "/var/run/secrets/restic-password"; + paths = [ + (builtins.toString deployment.dataDirectory) + ]; + repository = "s3:s3.us-west-004.backblazeb2.com/gleipnir-backup-deltamvcd/files"; + + }; + sops.secrets."fss-${deployment.customer}-env" = { + format = "dotenv"; + group = "${group}"; + mode = "0440"; + owner = "${user}"; + restartUnits = ["fss-${deployment.customer}-webserver.service"]; + sopsFile = ../../secrets/fieldseeker-sync/${deployment.customer}.env; + }; + systemd.services."fss-${deployment.customer}-audio-post-processor" = { + after=["network.target" "network-online.target" "fss-${deployment.customer}-migrate.service"]; + description="FieldSeeker sync audio post processor"; + requires=["network-online.target"]; + restartIfChanged = false; + stopIfChanged = false; + serviceConfig = { + EnvironmentFile="${environmentFile}"; + Type = "simple"; + User = "${user}"; + Group = "${group}"; + ExecStart = "${fieldseeker-sync-pkg}/bin/audio-post-processor"; + TimeoutStopSec = "5s"; + PrivateTmp = true; + WorkingDirectory = "/tmp"; + }; + startAt = "*:0/15"; + wantedBy = ["multi-user.target"]; + }; + systemd.services."fss-${deployment.customer}-export" = { + after=["network.target" "network-online.target" "fss-${deployment.customer}-migrate.service"]; + description="FieldSeeker sync periodic sync tool"; + requires=["network-online.target"]; + restartIfChanged = false; + stopIfChanged = false; + serviceConfig = { + EnvironmentFile="${environmentFile}"; + ExecStart = "${fieldseeker-sync-pkg}/bin/full-export"; + Group = "${group}"; + PrivateTmp = true; + TimeoutStopSec = "5s"; + Type = "simple"; + User = "${user}"; + WorkingDirectory = "/tmp"; + }; + startAt = "*:0/15"; + wantedBy = ["multi-user.target"]; + }; + systemd.services."fss-${deployment.customer}-migrate" = { + after=["network.target" "network-online.target"]; + description="FieldSeeker DB migrate"; + requires=["network-online.target"]; + serviceConfig = { + Environment="SENTRY_RELEASE=${inputs.fieldseeker-sync.rev}"; + EnvironmentFile="${environmentFile}"; + Type = "oneshot"; + User = "${user}"; + Group = "${group}"; + ExecStart = "${fieldseeker-sync-pkg}/bin/migrate"; + TimeoutStopSec = "5s"; + PrivateTmp = true; + WorkingDirectory = "/tmp"; + }; + wantedBy = ["multi-user.target"]; + }; + systemd.services."fss-${deployment.customer}-webserver" = { + after=["network.target" "network-online.target" "fss-${deployment.customer}-migrate.service"]; + description="FieldSeeker sync"; + requires=["network-online.target"]; + serviceConfig = { + Environment="SENTRY_RELEASE=${inputs.fieldseeker-sync.rev}"; + EnvironmentFile="${environmentFile}"; + Type = "simple"; + User = "${user}"; + Group = "${group}"; + ExecStart = "${fieldseeker-sync-pkg}/bin/webserver"; + TimeoutStopSec = "5s"; + PrivateTmp = true; + WorkingDirectory = "/tmp"; + }; + wantedBy = ["multi-user.target"]; + }; + users.groups.${group} = {}; + users.users.${user} = { + group = "${group}"; + isSystemUser = true; + }; + } + ) cfg.deployments + ); } diff --git a/modules/system/restic/default.nix b/modules/system/restic/default.nix index 847cb4a..2494ea6 100644 --- a/modules/system/restic/default.nix +++ b/modules/system/restic/default.nix @@ -7,4 +7,22 @@ with lib; imports = [ ./restic.nix ]; + config = { + sops.secrets.restic-env = { + format = "yaml"; + key = "backblaze"; + group = "root"; + mode = "0440"; + owner = "root"; + sopsFile = ../../../secrets/restic.yaml; + }; + sops.secrets.restic-password = { + format = "yaml"; + key = "password"; + group = "root"; + mode = "0440"; + owner = "root"; + sopsFile = ../../../secrets/restic.yaml; + }; + }; } diff --git a/roles/nidus-sync.nix b/roles/nidus-sync.nix index 3ad8a5e..380d825 100644 --- a/roles/nidus-sync.nix +++ b/roles/nidus-sync.nix @@ -1,4 +1,16 @@ { config, lib, pkgs, ... }: { myModules.caddy.enable = true; - myModules.fieldseeker-sync.enable = true; + myModules.fieldseeker-sync.deployments = [{ + customer = "deltamvcd"; + #database = "fieldseeker-sync"; + dataDirectory = /opt/fieldseeker-sync/deltamvcd; + port = 3000; + subdomain = "deltamvcd"; + } { + customer = "gleipnir-qa"; + #database = "fieldseeker-sync-gleipnir"; + dataDirectory = /opt/fieldseeker-sync/gleipnir; + port = 3001; + subdomain = "gleipnir-qa"; + }]; } diff --git a/secrets/fieldseeker-sync.env b/secrets/fieldseeker-sync/deltamvcd.env similarity index 100% rename from secrets/fieldseeker-sync.env rename to secrets/fieldseeker-sync/deltamvcd.env diff --git a/secrets/fieldseeker-sync/gleipnir-qa.env b/secrets/fieldseeker-sync/gleipnir-qa.env new file mode 100644 index 0000000..689d72a --- /dev/null +++ b/secrets/fieldseeker-sync/gleipnir-qa.env @@ -0,0 +1,28 @@ +FIELDSEEKER_SYNC_ARCGIS_TOKEN=ENC[AES256_GCM,data:2nGMctSAu2vtjxArncKBN5ko2mz1Des4svGTl0RWOevMmg6eoUtoMvgx4zj6MJtWsT4KgbACkVVBO0tQFrcPf9mCeqRRG8STn000AxX33IlDKbxm98xYLWu/tTUZM9Rtm5uPN6ZixX5ppQpLNT6ZNXp6qQJkLAoMZ2aPhVji2T+fThILZ6/jWR+Tb9xmeNdjWCHvl14axJV1fpeBzAAkTP5xkhkg8WIVHTe9NGYu9by75gtbNYkovbUtJkdHS19s/gFLDabRWgX5LM1FxBC5MsjkRilDGiX8Ys/cfPcEny7Y4erdABz5/23wQMJrR/XqANW1Whto/BpBVYJeuxoEetz+B43j9N3GlYHEuAAOXYrrhQ==,iv:xyi8I+/tgsBsAEwNB1Hl74J3K66rLVfM33zxZwz0WBA=,tag:Cf4MYljG/cWylFSc4xAnuA==,type:str] +FIELDSEEKER_SYNC_ARCGIS_TENANTID=ENC[AES256_GCM,data:Zc3qodyvIvG49pbTe0DRfmZT,iv:0kLZXrwkmXjd65ZWcP6K39oVDHlkB8KE3AC91p/xsCo=,tag:374VpZWprTsmMGgJnjiHmQ==,type:str] +FIELDSEEKER_SYNC_ARCGIS_SERVICEROOT=ENC[AES256_GCM,data:zZsg/B9ZdMQybxTGeQa55ZLJCMFau6Ephz8Dtgjd,iv:45mC2/kBS6Yf6CRy+4WH+8wuG0A1c/3OBNU4rpzGbtA=,tag:JHC0oHJnhR6H7VO2CgPnXA==,type:str] +FIELDSEEKER_SYNC_ARCGIS_FIELDSEEKERSERVICE=ENC[AES256_GCM,data:OFIYNlq2d7lDXp5vsoB7Sw==,iv:K7FB0pqc55PBsmeLmQZysXksyscYbZkDBVTJfX2faYM=,tag:woTG5DQOOQvPwirwz7wfYg==,type:str] +FIELDSEEKER_SYNC_DATABASE_URL=ENC[AES256_GCM,data:3VvsXW6eSIRKV04hW0eIlwB73/2pNLFpqZQgjH2VYwy+9XKmFuVzWq5gs43vFWumzNt3nSXplgE=,iv:H2YwilwJ0+taW3/KqmG8ZkuyRjNW1XbCL1i6vxxP/38=,tag:a2TRIlOIlhUKqEy+J+tanw==,type:str] +FIELDSEEKER_SYNC_USERFILES_DIRECTORY=ENC[AES256_GCM,data:9w2x5el/5tuRIVnlrBKt7irFFvnZEeYkZs3Q03BwSU1m,iv:U2+U4TINInZ3/EdjBs5Dj2Q5AxkGwXagLHEGyoT1j7Q=,tag:tQWtYEXEktxmd5appkSmdA==,type:str] +FIELDSEEKER_SYNC_WEBSERVER_BIND=ENC[AES256_GCM,data:bFcRwDBy+io5LxvtxnoDfw==,iv:9laF6OmbMK+CCs727cmm55zaJ/YlWiajX5pNThNeTRE=,tag:WlZur8M0ER9cQrozVOL4hA==,type:str] +FIELDSEEKER_SYNC_WEBHOOK_SECRET=ENC[AES256_GCM,data:3LL/GRSBYO6zi2jCiKDw/snVPOD5dA86yjGXsIEl+ObcfBmm5jQ=,iv:6z7pjBu3dQPbvPc4SCvKNzG2Fv3ro6FKxB9D9vQU00w=,tag:vaha53IJd0z5ifdssLGmNg==,type:str] +SENTRY_DSN=ENC[AES256_GCM,data:kvPQz0NKrUtGxISRdwIAlT+2Apxgb22GvGKVJLaxW47DoY4FzvxFh9cu0kAsnjcrQyee9Ol6F3l5tIWDacCQVp0plob4u4NSDvE=,iv:uNZPx+J1jjRDcwknw6XgeYrWk6/UYj7sf4YlkxQeYmM=,tag:57e9XEtFCWRIFEr7wLjh6g==,type:str] +SENTRY_ENVIRONMENT=ENC[AES256_GCM,data:7Wb+6AqT,iv:0zlDgfBbkOwWkYcyQZY9BKn/pIDGT4R7N35A/NOVC44=,tag:AdalaKwhhohIwQcTkroyww==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6WjBDZDJlVTdMOG9xdHYv\neXF6WEtROE5Pc001WVZNWHpiTjlrbkp4RGxFCnlwS3FQQVptRGRPKy9iZEhsUnhD\nWWdlYm5OYmdZUlIwWVJKUkVVYjgvalUKLS0tIFhmMU5FYzFsMHF1alg0Mk9MWllK\nK2RJZmpQd0Iwa29nd2o4eVY5MTZIR3MKGaZ44y7MK1C8lKfRcbD0rA6ZO2ypMtt1\nMJKabJaYVnPQhiGBxSyccXEtLtWwDSR+lax03v1tRvotebIj/w5emg==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1fnkhk9rv7r8gh84vxnhvndk4fgh20qcj4hvnfhdpumcydl6m6vrse50lrz +sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6VXZ0SXExWGpiRHAwRmMv\nZDF6NG1TcHlSbHhxSjQwaDZDTndhSDRycWhVCkk0WTB6NmVHZE1DU2VESXZ4aWo5\nTm9SVzNGQmE5a1RmQVZ5Y1RMNFR1c00KLS0tIHEzWTQ1aE5EV0dCTHJqcWdkd3ho\ncjRzWGFjVnBEM0tmdXlvSzVIWUdFUG8KvwyHbfnCEtU9ptKDkWiN5YO2iTnGulzc\nJOz5wrYZEQJci2M/IghcsiB8p802mMP6QuMV7eR3MN22y6W4XzulDA==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_1__map_recipient=age1x704pjnueguchkl54ly8w4w26ltys5900v7xnl7w3zlgasus09jszz45t8 +sops_age__list_2__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLZmdFYisxT0phajd2ZWFI\nUlpmaWE4STJTWTM5RW1EUWhsZmtUMXlFQUUwCjZFWlZJZWo5SHQyNjNIQml1Yzlp\nN0wvN21pc1dqMllsL3RZNlB6WXdTcWsKLS0tIE1JQ0tnY2x4bVpZYjNqNzJIajNI\nRCtGWTdQM1F2RFZuTENPUXlQN24yR1UKtVUQT4iwhtfFuYUhYJeWRjdWyHv5slhZ\nCHbiuT6w2CW50sEX1NEdLCrIrYwjfKJ7lTCVVj9pD7p4Vz3Qfs1b2w==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_2__map_recipient=age15y4k929zaj9fdg3vd40pa40tgvrgv9mn22xfummn5zxfmkcw5d0st6prjx +sops_age__list_3__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPRy9nVUZTandIR3QxcUlB\nQjNLZzFLZ3JMMlJ2NVB6M0c5eUZXQWwxckNBCllHZndPV3FnalNQdVZlSHRUVUhL\nZ0orSFpJZ0RsZXpEYndLYllFM3NJNzAKLS0tIG5MWWRnSGt2bnY3b3Z3aVhoQzRt\nWk1nQVN6Y0JHN0cvYU9CVjNqdEJzdDgKN05f7TZpMBTs45EAvjCPMO1BB4rErNJa\nisleEfoZjO2Q2Pu6GUIrm2JBNv3dEZpA2iIhHp7Q0hYN8wQp7ZBVhQ==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_3__map_recipient=age1ck44jqpuz3zlthquvuh7wsemrjrgfzhn462sk7rlfetwxpgy0uqs79xn2h +sops_age__list_4__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1VjV0WUVEZEtsOVNGcWsr\nZ0wzWitIOXZMYkV2ZWUvWVBmSEpWWVlOQkRzCkUvWDcvT0NaNXZWSE5xUDFrdm9h\nWnRVbTVHWHU5SEt4T2lzZzV0SFBaQUEKLS0tIEZGc01iZG1EeXRqU0NIMVpoK0tq\nemZEeGJYKy9PMG5hS3F2Z1dBOGpOWEkKiTiM6AKal3aAWvaBt0/wEn3M4afP4xEV\nF2aMsrWZcNrVFXaErAsQ41DORW6hBnSEhTobHpWqjwkrz/uf8pm9jA==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_4__map_recipient=age1z87rm3qrrspv44nsg73ntn9zys5g498lry4t4cy6x9hffapug5cqfprntm +sops_age__list_5__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4ckhkM1J2eElBTkI3VlIr\neFNCNElhbVR0S1Y0WEdsMU1YTnhPbEx5ZkNNClpOa1N6alpIQ3NJTmZJV0dZT2dC\nVFBlQWdvQk5VY0RWbkRFZ0RGRm1pYjQKLS0tIEF2SWsrc3EvTWlGR2htc0poSFhJ\nSU9od3lkRjcvVVlvdUIwaVdvR2xhMVkKQgiSBG2yCiNoBi1hSRmgpW86qoDVy9gV\nMqXhcQ79C9tozh4lrHU8HI9hLex2OdZYlvoJfnssPCJcrj1NgPvTZQ==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_5__map_recipient=age1t3ryfktuhr3cysf49m9q2n8fkjf9ajjjnhztxw9hz8paxgk4lpcq065jge +sops_age__list_6__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzd0dFWlQ3bXYvRTBCZDN0\nWU0wM2VWbHFuYm1yby8vSkQwSHNmRkxVN1Q0CngrZWxYSUdUcFBSMUkyV3BzM0ZX\nczhpbklDQ0p3bENHOGpNdkhwR21LaVkKLS0tIE9jTlE5cUY2WkFsYXJsOUdtUlhi\na0ZJVmtVS25wOEEwRGJWcFdpMFVPYmsKTrrs/EZMxZqCCq+6Tbm0ppfRaDfiaFdj\nzn8yhsl7VVZdQ10qmee3LLUFGTrkFx+hrcHCxWAvF7QaSwBg7Jqk3A==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_6__map_recipient=age1j90h7hcp4fctr2xwj4zf9cxuelm43wkujvryc9hk6rzzc37rwdmss035w7 +sops_lastmodified=2025-09-30T17:39:37Z +sops_mac=ENC[AES256_GCM,data:Q49cycoyYuo2fvZZCIaVikwd7+uqof5pek98HeeNE7xvPims0juwfVfdaWVTJS3A00Ci+e3lg4QY82snqEB9ajH2dNaEdS+h7BIDAf1DXp15StN8iEyAG3iOJhwW3hj6kH4LUhNWFS88VGQJFMNh5dT+oyhO4EuI6yVy3BBlI+o=,iv:11oYVnndK63rztVBNB58cnNbgSWb2gSDpFPlE9BhwwQ=,tag:5fOLD23wR6deq50TDGKjYw==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.10.2