Install postgres and dendrite
diff --git a/flake.lock b/flake.lock
index a2bb784..bf6af46 100644
--- a/flake.lock
+++ b/flake.lock
@@ -150,9 +150,33 @@
"home-manager": "home-manager",
"nixpkgs": "nixpkgs_2",
"nixpkgs-unstable": "nixpkgs-unstable",
+ "scalpel": "scalpel",
"sops-nix": "sops-nix"
}
},
+ "scalpel": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "sops-nix": [
+ "sops-nix"
+ ]
+ },
+ "locked": {
+ "lastModified": 1655566736,
+ "narHash": "sha256-Vyolf8j7QcAHyVTQMqS8EGYZ/P6leJtiVNhYyIxoMRw=",
+ "owner": "polygon",
+ "repo": "scalpel",
+ "rev": "16c2103d613bb1c7adc6dbf2a17c2980ce08567f",
+ "type": "github"
+ },
+ "original": {
+ "owner": "polygon",
+ "repo": "scalpel",
+ "type": "github"
+ }
+ },
"sops-nix": {
"inputs": {
"nixpkgs": [
diff --git a/flake.nix b/flake.nix
index d2be14d..c897bc8 100644
--- a/flake.nix
+++ b/flake.nix
@@ -7,13 +7,17 @@
inputs.deploy-rs.url = "github:serokell/deploy-rs";
inputs.home-manager.url = "github:nix-community/home-manager/release-22.11";
inputs.sops-nix.url = "github:Mic92/sops-nix";
+ inputs.scalpel.url = "github:polygon/scalpel";
inputs.home-manager.inputs.nixpkgs.follows = "nixpkgs";
inputs.home-manager.inputs.utils.follows = "deploy-rs/utils";
inputs.sops-nix.inputs.nixpkgs.follows = "nixpkgs";
- outputs = { self, nixpkgs, deploy-rs, home-manager, sops-nix, nixpkgs-unstable, ... }@inputs:
+ inputs.scalpel.inputs.nixpkgs.follows = "nixpkgs";
+ inputs.scalpel.inputs.sops-nix.follows = "sops-nix";
+
+ outputs = { self, nixpkgs, deploy-rs, home-manager, sops-nix, scalpel, nixpkgs-unstable, ... }@inputs:
let
system = "x86_64-linux";
pkgs = import nixpkgs {
@@ -27,35 +31,47 @@
in
{
nixosConfigurations.clicks =
- nixpkgs.lib.nixosSystem {
- inherit system pkgs;
+ let
+ base = nixpkgs.lib.nixosSystem {
+ inherit system pkgs;
+ modules = [
+ ./default/configuration.nix
+ ./default/hardware-configuration.nix
+ ./modules/caddy.nix
+ ./modules/clamav.nix
+ ./modules/code-server.nix
+ ./modules/dmarc.nix
+ ./modules/dnsmasq.nix
+ ./modules/doas.nix
+ ./modules/docker.nix
+ ./modules/ecryptfs.nix
+ ./modules/fail2ban.nix
+ ./modules/fuck.nix
+ ./modules/git.nix
+ ./modules/grafana.nix
+ ./modules/home-manager-users.nix
+ ./modules/kitty.nix
+ ./modules/matrix.nix
+ ./modules/mongodb.nix
+ ./modules/node.nix
+ ./modules/postgres.nix
+ ./modules/samba.nix
+ ./modules/scalpel.nix
+ ./modules/tesseract.nix
+ sops-nix.nixosModules.sops
+ {
+ users.mutableUsers = false;
+ _module.args = { inherit pkgs-unstable; };
+ }
+ ];
+ specialArgs = { base = null; };
+ };
+ in
+ base.extendModules {
modules = [
- ./default/configuration.nix
- ./default/hardware-configuration.nix
- ./modules/caddy.nix
- ./modules/clamav.nix
- ./modules/code-server.nix
- ./modules/dmarc.nix
- ./modules/dnsmasq.nix
- ./modules/doas.nix
- ./modules/docker.nix
- ./modules/ecryptfs.nix
- ./modules/fail2ban.nix
- ./modules/fuck.nix
- ./modules/git.nix
- ./modules/grafana.nix
- ./modules/home-manager-users.nix
- ./modules/kitty.nix
- ./modules/mongodb.nix
- ./modules/node.nix
- ./modules/samba.nix
- ./modules/tesseract.nix
- sops-nix.nixosModules.sops
- {
- users.mutableUsers = false;
- _module.args = { inherit pkgs-unstable; };
- }
+ scalpel.nixosModules.scalpel
];
+ specialArgs = { inherit base; };
};
deploy.nodes.clicks = {
diff --git a/modules/grafana.nix b/modules/grafana.nix
index 69c231e..6aee70f 100644
--- a/modules/grafana.nix
+++ b/modules/grafana.nix
@@ -1,15 +1,26 @@
-{
+{ lib, config, ... }: {
services.grafana = {
enable = true;
settings = {
server = rec {
domain = "logs.clicks.codes";
- root_url ="https://${domain}";
+ root_url = "https://${domain}";
http_port = 9052;
enable_gzip = true;
};
analytics.reporting_enabled = false;
};
+
+ provision.datasources.settings.datasources = [{
+ name = "clicks-postgresql";
+ type = "postgres";
+ access = "proxy";
+
+ url = "postgres://localhost:${toString config.services.postgresql.port}";
+ user = "clicks_grafana";
+ password = "$__file{${config.sops.secrets.clicks_grafana_db_password.path}}";
+ # defined in postgres.nix
+ }];
};
}
diff --git a/modules/home-manager-users.nix b/modules/home-manager-users.nix
index e83e0cb..a3f450e 100644
--- a/modules/home-manager-users.nix
+++ b/modules/home-manager-users.nix
@@ -1,6 +1,6 @@
# Home manager is used separately from this deploy, but we still need to create
# user accounts in the system config
-{ pkgs, lib, ... }:
+{ base, pkgs, lib, config, ... }:
let
mkUser = username: {
isSystemUser = true;
@@ -9,7 +9,11 @@
home = "/services/${username}";
group = "clicks";
shell = pkgs.bashInteractive;
- };
+ } // (
+ if builtins.pathExists "${../services}/${username}/system.nix"
+ then import "${../services}/${username}/system.nix"
+ else { }
+ );
in
{
imports = [
@@ -26,4 +30,14 @@
(map (name: { inherit name; value = mkUser name; }))
builtins.listToAttrs
];
-}
+} // (
+ if (base != null)
+ then {
+/* users.groups = lib.mapAttrs'
+ (_: user: {
+ name = user.group;
+ value = { };
+ })
+ base.config.users.users;*/
+ } else { }
+)
diff --git a/modules/matrix.nix b/modules/matrix.nix
new file mode 100644
index 0000000..f669943
--- /dev/null
+++ b/modules/matrix.nix
@@ -0,0 +1,103 @@
+{ base, config, lib, pkgs, ... }:
+let
+ postgresUrlFor = service:
+ "postgres://dendrite:!!dendrite_db_password!!@localhost:${toString config.services.postgresql.port}/dendrite_${service}?sslmode=disable";
+in
+{
+ services.dendrite = {
+ enable = true;
+ httpPort = 4527;
+ settings = {
+ global = {
+ server_name = "coded.codes";
+ private_key = config.sops.secrets.matrix_private_key.path;
+ };
+ user_api = {
+ account_database.connection_string = postgresUrlFor "account_database";
+ device_database.connection_string = postgresUrlFor "device_database";
+ };
+ sync_api = {
+ search.enable = true;
+ database.connection_string = postgresUrlFor "sync_api";
+ };
+ room_server.database.connection_string = postgresUrlFor "room_server";
+ mscs.database.connection_string = postgresUrlFor "mscs";
+ media_api.database.connection_string = postgresUrlFor "media_api";
+ key_server.database.connection_string = postgresUrlFor "key_server";
+ federation_api.database.connection_string = postgresUrlFor "federation_api";
+ app_service_api.database.connection_string = postgresUrlFor "app_service_api";
+
+ client_api.registration_shared_secret = "!!registration_shared_secret!!";
+ };
+ };
+
+ users.users.dendrite = {
+ isSystemUser = true;
+ createHome = true;
+ home = config.systemd.services.dendrite.serviceConfig.WorkingDirectory;
+ group = "clicks";
+ shell = pkgs.bashInteractive;
+ };
+
+ systemd.services.dendrite.serviceConfig = {
+ DynamicUser = lib.mkForce false;
+ User = lib.mkForce config.users.users.dendrite.name;
+ Group = lib.mkForce config.users.users.dendrite.group;
+ };
+
+ sops.secrets = (lib.pipe [
+ "registration_shared_secret"
+ ] [
+ (map (name: {
+ inherit name;
+ value = {
+ mode = "0400";
+ owner = config.users.users.root.name;
+ group = config.users.users.nobody.group;
+ sopsFile = ../secrets/matrix.json;
+ format = "json";
+ };
+ }))
+ builtins.listToAttrs
+ ]) // {
+ matrix_private_key = {
+ mode = "0400";
+ owner = config.users.users.dendrite.name;
+ group = config.users.users.dendrite.group;
+ sopsFile = ../secrets/matrix_private_key.pem;
+ format = "binary";
+ };
+ };
+} // (
+ let
+ isDerived = base != null;
+ in
+ if isDerived
+ # We cannot use mkIf as both sides are evaluated no matter the condition value
+ # Given we use base as an attrset, mkIf will error if base is null in here
+ then
+ let
+ ExecStartPre = "${base.config.systemd.services.dendrite.serviceConfig.ExecStartPre}";
+ dendrite_cfgfile = builtins.head (builtins.match ".*-i ([^[:space:]]+).*" "${ExecStartPre}");
+ in
+ {
+ scalpel.trafos."dendrite.yaml" = {
+ source = dendrite_cfgfile;
+ matchers."dendrite_db_password".secret =
+ config.sops.secrets.dendrite_db_password.path; # Defined in postgres.nix
+ matchers."registration_shared_secret".secret =
+ config.sops.secrets.registration_shared_secret.path;
+ owner = config.users.users.dendrite.name;
+ group = config.users.users.dendrite.group;
+ mode = "0400";
+ };
+
+ systemd.services.dendrite.serviceConfig.ExecStartPre = lib.mkForce (
+ builtins.replaceStrings
+ [ "${dendrite_cfgfile}" ]
+ [ "${config.scalpel.trafos."dendrite.yaml".destination}" ]
+ "${ExecStartPre}"
+ );
+ }
+ else { }
+)
diff --git a/modules/postgres.nix b/modules/postgres.nix
new file mode 100644
index 0000000..63e7697
--- /dev/null
+++ b/modules/postgres.nix
@@ -0,0 +1,84 @@
+{ lib, config, pkgs, ... }: {
+ services.postgresql = {
+ enable = true;
+
+ package = pkgs.postgresql;
+ settings = {
+ log_connections = true;
+ log_statement = "all";
+ logging_collector = true;
+ log_disconnections = true;
+ log_destination = lib.mkForce "syslog";
+ };
+
+ ensureUsers = [
+ {
+ name = "clicks_grafana";
+ ensurePermissions = {
+ "ALL TABLES IN SCHEMA public" = "SELECT";
+ "SCHEMA public" = "USAGE";
+ };
+ }
+ {
+ name = "dendrite";
+ ensurePermissions = {
+ "DATABASE dendrite_account_database" = "ALL PRIVILEGES";
+ "DATABASE dendrite_device_database" = "ALL PRIVILEGES";
+ "DATABASE dendrite_sync_api" = "ALL PRIVILEGES";
+ "DATABASE dendrite_room_server" = "ALL PRIVILEGES";
+ "DATABASE dendrite_mscs" = "ALL PRIVILEGES";
+ "DATABASE dendrite_media_api" = "ALL PRIVILEGES";
+ "DATABASE dendrite_key_server" = "ALL PRIVILEGES";
+ "DATABASE dendrite_federation_api" = "ALL PRIVILEGES";
+ "DATABASE dendrite_app_service_api" = "ALL PRIVILEGES";
+ };
+ }
+ ] ++ (map
+ (name: (
+ {
+ inherit name;
+ ensurePermissions = { "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; };
+ }
+ )) [ "minion" "coded" "pinea" ]);
+
+ ensureDatabases = [
+ "dendrite_account_database"
+ "dendrite_device_database"
+ "dendrite_sync_api"
+ "dendrite_sync_api"
+ "dendrite_room_server"
+ "dendrite_mscs"
+ "dendrite_media_api"
+ "dendrite_key_server"
+ "dendrite_federation_api"
+ "dendrite_app_service_api"
+ ];
+ };
+
+ systemd.services.postgresql.postStart = lib.mkAfter (lib.pipe [
+ { user = "clicks_grafana"; passwordFile = config.sops.secrets.clicks_grafana_db_password.path; }
+ { user = "dendrite"; passwordFile = config.sops.secrets.dendrite_db_password.path; }
+ ] [
+ (map (userData: ''
+ $PSQL -tAc "ALTER USER ${userData.user} PASSWORD '$(cat ${userData.passwordFile})';"
+ ''))
+ (lib.concatStringsSep "\n")
+ ]);
+
+ sops.secrets = lib.pipe [
+ "clicks_grafana_db_password"
+ "dendrite_db_password"
+ ] [
+ (map (name: {
+ inherit name;
+ value = {
+ mode = "0400";
+ owner = config.services.postgresql.superUser;
+ group = config.users.users.${config.services.postgresql.superUser}.group;
+ sopsFile = ../secrets/postgres.json;
+ format = "json";
+ };
+ }))
+ builtins.listToAttrs
+ ];
+}
diff --git a/modules/scalpel.nix b/modules/scalpel.nix
new file mode 100644
index 0000000..569d2b4
--- /dev/null
+++ b/modules/scalpel.nix
@@ -0,0 +1,12 @@
+{ lib, config, ... }: let
+ cfg = config.scalpel;
+in {
+ system.activationScripts.scalpelCreateStore.text = lib.mkForce ''
+ echo "[scalpel] Ensuring existance of ${cfg.secretsDir}"
+ mkdir -p ${cfg.secretsDir}
+ grep -q "${cfg.secretsDir} ramfs" /proc/mounts || mount -t ramfs none "${cfg.secretsDir}" -o nodev,nosuid,mode=0751
+
+ echo "[scalpel] Clearing old secrets from ${cfg.secretsDir}"
+ find '${cfg.secretsDir}' -wholename '${cfg.secretsDir}' -o -prune -exec rm -rf -- {} +
+ '';
+}
diff --git a/secrets/matrix.json b/secrets/matrix.json
new file mode 100644
index 0000000..680e715
--- /dev/null
+++ b/secrets/matrix.json
@@ -0,0 +1,24 @@
+{
+ "registration_shared_secret": "ENC[AES256_GCM,data:Kg9BvMxn2/QqbU5C6OnMrlvT8uFmtu3v3UMb+OqqHNSxEtLcXRPMw304+QeTYIg7m9gfZ0m3i3fUnRgya0+Lhg==,iv:2PnEIgd7kicBrR9fdIDJ7j4YklalWxn2BrrS4ipTL2Q=,tag:tgxTZGEAAEZdaqu5kh3a6w==,type:str]",
+ "sops": {
+ "kms": null,
+ "gcp_kms": null,
+ "azure_kv": null,
+ "hc_vault": null,
+ "age": [
+ {
+ "recipient": "age15mv77dpnh5762gk5rsw2u79uza4tg8cu6r3nlwjudlzmdqqck3ss6mg9dy",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNYjhKSVpncnhMdmpyV01U\naWgvYzZOZ1AxNHpnUWZvaTNtczdQT0RPcWhnCm1yaGJLdWNvOGVTWEpwTkdSV2t3\nTUtqZlk1bHcybDlNaWtFbnJHVmpyUW8KLS0tIC9yQUZ0dTFjOGRrZTBGVEZvNzFq\nbjgxbkFyZTUreWg2U3B2RU9RSVJhV0kKkvw+LLVg6cw5suarhIhIU4CjcJH3fW3Q\nCnhbNzHSPNrdNJi98g+r5JyjeIlDgd64xrmFD4Ef70ABEiybVLgy5A==\n-----END AGE ENCRYPTED FILE-----\n"
+ },
+ {
+ "recipient": "age1fxxnmkeuqhhct93c43pwkzhuzzq8857s5hye6pgfpku70kjn4ecqtamfqr",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvK25uL25GVHVkNmlNVjZq\nVFgvQXVtcFVhMEQ2QlQ3alcyWXRTU2xXNXhNClBzY1hXblJ4b2QxNFlycFdreldQ\nRWhFMjBNN1JjRjg1TUJJRUlqbHRZZHMKLS0tIGZxVXJtMGtlZG5aTStJQUEzYi9H\nRm1JOEgrelZPUXJNMkppVVVROUpFSEUKpDpQ+CclvVsdEf1JD/EmrRVmsbj24VPM\nn4jfNwDDWTn2kflz2p/TiVdEXskqxdR81EjmnhSZw5lnqF206p8AdQ==\n-----END AGE ENCRYPTED FILE-----\n"
+ }
+ ],
+ "lastmodified": "2023-05-19T11:14:54Z",
+ "mac": "ENC[AES256_GCM,data:BgaUXQUgtbQasC3lnUoMPOMtxb4/oZ+0PezXL0ixf3wDHZ0WUYoOxH1OhaagTbU1XJMvvMv3O4ktALrKxk3dNUFlPoLWHOey9FndbGqlpdxFr4SbSWPzimXTepT+uSHmHJ38bzn2AHbI0ycrntJqEvtidf4NC/5X5U7HTDhYuYs=,iv:xiLvQrG2EIiX/eHX9BVITar+Hm5gbtqOfnSJuwPnmcc=,tag:zegDNHVNeGdCgavnxvap/w==,type:str]",
+ "pgp": null,
+ "unencrypted_suffix": "_unencrypted",
+ "version": "3.7.3"
+ }
+}
\ No newline at end of file
diff --git a/secrets/matrix_private_key.pem b/secrets/matrix_private_key.pem
new file mode 100644
index 0000000..c28a422
--- /dev/null
+++ b/secrets/matrix_private_key.pem
@@ -0,0 +1,24 @@
+{
+ "data": "ENC[AES256_GCM,data:PKyWohFo987nrxaUxAEkVqyoqK5KDvNE9sJ/Mfqnh/VGQkETpiH+1RUmo9bJ/yghk9oEAzoKGig5kty9DJn6ge1y2/aV2HuwWhJtNGrPQmNle2KArR8R7zGLTIW8BONlmzz8CiXyzEJA+VxlacVYIEwTpdkhmCIq86ZOR5XG8mpI2LR8onZf1u0=,iv:xRGXx8jjK4IbrDRX5US5h+4rBEHhQshBXS+ag9mdfw8=,tag:MsUY0EB8k2dq4xDTRmeUEg==,type:str]",
+ "sops": {
+ "kms": null,
+ "gcp_kms": null,
+ "azure_kv": null,
+ "hc_vault": null,
+ "age": [
+ {
+ "recipient": "age15mv77dpnh5762gk5rsw2u79uza4tg8cu6r3nlwjudlzmdqqck3ss6mg9dy",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvcFEyTzJkV2UrbFd6UTFR\nT2EvMjRUcXlEUGJaSkxDRUgrb2FWUVlrOFNBCjRIM0RKM0dwV295cjFzMjNiVFNv\nRjlCcTBJMFc0UjkzUUN6SXRwSHFOazAKLS0tIFZBVmNmVnBEdVpzQzRCM2xFdEhH\nN3NSZGFlVUxvSW0yL2Q4M2owY3l0Y3cK25ylhmfVmV1GimAEBysntMcbHJeT1cso\n0MfqBvRVFxOx1Ewi8uE6vUXzO1mlUOjt21FeaWSdUq/2oKpKtr9qZw==\n-----END AGE ENCRYPTED FILE-----\n"
+ },
+ {
+ "recipient": "age1fxxnmkeuqhhct93c43pwkzhuzzq8857s5hye6pgfpku70kjn4ecqtamfqr",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxZm1ycXhVeEFCenFlcUQy\nWGEzN0cxYkd1alVsbkcxN25jK0paQjl4azBNClp0OVhXV1RjNlVOVDJObE1iYWd2\nSzVIUVRKUlc5ZEI0ZnVuWklhVG1zNDQKLS0tIDBpZFJaZzhxMzhad2tlWDFQNUJI\nVjdIblVqS1FhRTVHUkVoQXF5MGNwaDQK18s1piDOpkneG/Z+8CIW4X2c33QciDSD\nSgO3BIxY1KhGsRN/5LL7622WIeRjtMikysEcUYsG34XbEbUp7E7ibw==\n-----END AGE ENCRYPTED FILE-----\n"
+ }
+ ],
+ "lastmodified": "2023-05-19T12:16:14Z",
+ "mac": "ENC[AES256_GCM,data:WeeZ1Yoj9aKzmdlvNBn3BPCD07GYhwXCs09PPgh0QtTtydmy4hYZRXQWnnwmA6nwSh1un5CZ9ZoCMXp9TPdJh6o8C5hqNV7sj+763nMFIycFu/u4dDtnQyawdhibHuqFM6yH0W0ZQ2r+6RdL9RpTPZqBkFuNVrKSh4jiVas7TgU=,iv:jOSlWvT9+AdO0xIGhJlR3yohjZs/eybIczn4i7Z39k8=,tag:JhheDVX1K61O+v5y3x4jXg==,type:str]",
+ "pgp": null,
+ "unencrypted_suffix": "_unencrypted",
+ "version": "3.7.3"
+ }
+}
\ No newline at end of file
diff --git a/secrets/postgres.json b/secrets/postgres.json
new file mode 100644
index 0000000..5755938
--- /dev/null
+++ b/secrets/postgres.json
@@ -0,0 +1,25 @@
+{
+ "clicks_grafana_db_password": "ENC[AES256_GCM,data:0iiAk7Stag5pRLJgRac6XF57CWz4VIW4KPW6Q9iZxgA6FaQmhK0c9cLMpfsNpBn0LNqjxT/rjBveB0tKLMeIaTlMnGAOstMHiPY4q7/bnCEu8QXY3lDmz881Jq3JSuQrbZaiEWVS35jHsz+Gbb4q3m+TKGR4KWCkngF4mhbrMZESYixe2GgAnPbX/R+zs+HiQR/Z0x8PfjXiV56Kcqh0uQoVVEyusni64YtxHfA3bOkLQ+Prieq6yTLhdxKpsv3dtpKbj9GXD/p73KaZlWKr5TIDT+7NYuoK77OsSAGnOFjE4jd38fzk4El1e7qpc6Gk3w2wr72o5syVIQxOYBDGng==,iv:HYWWOPUgM3JCSvLFeRxXT55KjsFDN5kMSZXuOl2mO/g=,tag:wZmsdpmXUfEbqlePwko9hw==,type:str]",
+ "dendrite_db_password": "ENC[AES256_GCM,data:cCDapkXgw+tq37NrzbemcR68clsHmOuDdncpCVydtK3Wc4sjw4+1V/TYDvFsBIP+0YJPvIdNGh7QtLpl1HJD1HMHQM1wIOu0Bp4pjYA+OU7XiBXxgzrZcrQ2WyjVTa+Oh/8k16C7ZhyidAvQJF76mZjCEmoX9/yxNNQQ8vcdSYC6Z8vRNY/X4fGKAnXnNp4DsjEOqs8/WvvLAfgqCH7EcNc/ZWM8AjDR7fm+zFh7hGkSetthSOzjT7BmD61yYKYxGuYuKaBi2QFkSRlepAhAJ3GjS4xL03znZ8Mt1p7CoyARkSGoQPqrM3WKHxfpXUZOJSxFTmYOd9h9vcv+aJtXfA==,iv:t5UULLnERyMhZ6Kp+Hrb3138nMJyOBLOjQ8QqkrTHjw=,tag:AQQwn51asl39SK0SWMmb3g==,type:str]",
+ "sops": {
+ "kms": null,
+ "gcp_kms": null,
+ "azure_kv": null,
+ "hc_vault": null,
+ "age": [
+ {
+ "recipient": "age15mv77dpnh5762gk5rsw2u79uza4tg8cu6r3nlwjudlzmdqqck3ss6mg9dy",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLZ09qSTZ0UE52bWdoVG14\nZE1GNnBMWXM1Qy9rMTlEU3JBbEJhM3hHdFZVCnFRZHNwK1AwUzFHT1VwM3Y4TnFU\nOG9TUFFqU3RJaVpPUTZTVmdYaEFwL2cKLS0tIGhENFNmSTdLbXdwWVZYQjJnOG13\nQkhPZERQaEpqS2VsWUZTSU5TZ2FSSW8K3aeCAWFK2g8ho3qIwIDVjE5xG3vsSeQh\nz4TDQQyZF3QRtOtiqmG6xn5FJnQYATiOLtbTfJhBbHM++0TLwHTXsg==\n-----END AGE ENCRYPTED FILE-----\n"
+ },
+ {
+ "recipient": "age1fxxnmkeuqhhct93c43pwkzhuzzq8857s5hye6pgfpku70kjn4ecqtamfqr",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwNVB5L3hPcUFuaU1qNzdX\nL3psaUdpcHp1WkxmYS9Sdi9IY1BLbEd0aHdRCkp1TWlEWHY1RERMNWltK3N4a3d3\nYzhENENNYnVlaDdBcXBYYnQwSWRkTGMKLS0tIDlvWWlwNkNleVB5Y0RpZGJhRUR4\ndlVzY2plQ2loU2YwZXpIRnh2UFVFZTgKrgf/fmoatZbtnUSn1zVs0UJdcyipCqn7\nFh+K3dbT25JYD2U4glE3xW6D9TMTUBNGguHE3MQPJaK74FfGp7L/Bw==\n-----END AGE ENCRYPTED FILE-----\n"
+ }
+ ],
+ "lastmodified": "2023-05-19T12:28:32Z",
+ "mac": "ENC[AES256_GCM,data:piOQ+0xxTxNvrpEXMxvPoglE9/uN4xseGfuavvluGl+koSi/Orde/cl6Ck9p8jl+bxc5Vs44qQcxIHAtjnXw1PrnD900yTELtZygtZkCdyBYBugUaP8sF/FJoorIBcPOhx0ld0Z+UdX+IdiPsyjZ9STvn2zBmnnSLOVW/qAp2Qs=,iv:snLwST4vK96HEL+//+k3ffYvRFMTtKPIlNHpnmesNIY=,tag:5g8Ja0BpG+xkABnOspC71A==,type:str]",
+ "pgp": null,
+ "unencrypted_suffix": "_unencrypted",
+ "version": "3.7.3"
+ }
+}
\ No newline at end of file