diff --git a/.gitignore b/.gitignore index 217032a..c56a9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .direnv .pre-commit-config.yaml result -.manifests diff --git a/README.md b/README.md index cbf1cd4..19e76c3 100644 --- a/README.md +++ b/README.md @@ -5,84 +5,74 @@ We use [Kubenix](https://kubenix.org/) to write Kubernetes deployments in Nix! ## Images used Legend: - -- ✨: Image built with Nix (including - [NixNG](https://github.com/nix-community/NixNG)) +- ✨: Image built with Nix (including [NixNG](https://github.com/nix-community/NixNG)) - ✅: Official image or trusted publisher - 🫤: Unofficial image -| Status | Image | Comments | -| ------ | ---------------------------------------------- | --------------------------------------------------------- | -| ✨ | `nixng-blog` | | -| ✨ | `nixng-dnsmasq` | | -| ✨ | `nixng-attic` | | -| ✨ | `nixng-ntfy-sh` | | -| ✨ | `nixng-radicale` | | -| ✨ | `nixng-jellyseerr` | | -| ✨ | `nixng-radarr` | | -| ✨ | `nixng-sonarr` | | -| ✨ | `nixng-bazarr` | | -| ✨ | `nixng-prowlarr` | | -| ✨ | `nixng-deluge` | | -| ✨ | `nixng-mealie` | | -| ✨ | `nixng-atuin` | | -| ✅ | `jellyfin/jellyfin` | | -| ✅ | `postgres:14` | Database for Atuin | -| ✅ | `ghcr.io/paperless-ngx/paperless-ngx` | | -| ✅ | `docker.io/library/redis:7` | Database for Paperless-ngx | -| ✅ | `nextcloud` | | -| ✅ | `postgres:15` | Database for Attic, Nextcloud, Paperless-ngx and Hedgedoc | -| ✅ | `inbucket/inbucket` | | -| ✅ | `lscr.io/linuxserver/syncthing` | | -| ✅ | `codeberg.org/forgejo/forgejo` | | -| ✅ | `pihole/pihole` | | -| ✅ | `ghcr.io/immich-app/immich-server` | | -| ✅ | `ghcr.io/immich-app/immich-machine-learning` | | -| ✅ | `docker.io/redis:6.2-alpine` | Database for Immich | -| ✅ | `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` | Database for Immich | -| ✅ | `tombursch/kitchenowl` | | -| ✅ | `freshrss/freshrss` | | -| ✅ | `ubuntu/bind9` | | -| ✅ | `quay.io/hedgedoc/hedgedoc` | | -| 🫤 | `itzg/minecraft-server` | | -| 🫤 | `teddysun/kms` | | -| 🫤 | `mpepping/cyberchef` | | +| Status | Image | Comments | +| --- | --- | --- | +| ✨ | `nixng-blog` | | +| ✨ | `nixng-dnsmasq` | | +| ✨ | `nixng-attic` | | +| ✨ | `nixng-ntfy-sh` | | +| ✨ | `nixng-radicale` | | +| ✨ | `nixng-jellyseerr` | | +| ✨ | `nixng-radarr` | | +| ✨ | `nixng-sonarr` | | +| ✨ | `nixng-bazarr` | | +| ✨ | `nixng-prowlarr` | | +| ✅ | `jellyfin/jellyfin` | | +| ✅ | `linuxserver/deluge` | | +| ✅ | `ghcr.io/atuinsh/atuin` | | +| ✅ | `postgres:14` | Database for Atuin | +| ✅ | `ghcr.io/paperless-ngx/paperless-ngx` | | +| ✅ | `docker.io/library/redis:7` | Database for Paperless-ngx | +| ✅ | `nextcloud` | | +| ✅ | `postgres:15` | Database for Attic, Nextcloud, Paperless-ngx and Hedgedoc | +| ✅ | `inbucket/inbucket` | | +| ✅ | `lscr.io/linuxserver/syncthing` | | +| ✅ | `codeberg.org/forgejo/forgejo` | | +| ✅ | `pihole/pihole` | | +| ✅ | `ghcr.io/immich-app/immich-server` | | +| ✅ | `ghcr.io/immich-app/immich-machine-learning` | | +| ✅ | `docker.io/redis:6.2-alpine` | Database for Immich | +| ✅ | `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` | Database for Immich | +| ✅ | `tombursch/kitchenowl` | | +| ✅ | `freshrss/freshrss` | | +| ✅ | `ubuntu/bind9` | | +| ✅ | `quay.io/hedgedoc/hedgedoc` | | +| 🫤 | `itzg/minecraft-server` | | +| 🫤 | `teddysun/kms` | | +| 🫤 | `mpepping/cyberchef` | | ## Acknowledgements -- [dns.nix](https://github.com/kirelagin/dns.nix): A Nix DSL for defining DNS - zones -- [flake-utils](https://github.com/numtide/flake-utils): Handy utilities to - develop Nix flakes -- [kubenix](https://kubenix.org/): Declare and deploy Kubernetes resources using - Nix +- [dns.nix](https://github.com/kirelagin/dns.nix): A Nix DSL for defining DNS zones +- [flake-utils](https://github.com/numtide/flake-utils): Handy utilities to develop Nix flakes +- [kubenix](https://kubenix.org/): Declare and deploy Kubernetes resources using Nix - [nixhelm](https://github.com/farcaller/nixhelm): Nix-digestible Helm charts - [sops-nix](https://github.com/Mic92/sops-nix): Sops secret management for Nix ## Prerequisites -To deploy to the Kubernetes cluster, first make sure you have an admin account -on the cluster. You can generate this using -`nix run '.#gen-k3s-cert' ~/.kube`, assuming you have -SSH access to the master node. This puts a private key, signed certificate and a -kubeconfig in the kubeconfig directory +To deploy to the Kubernetes cluster, first make sure you have an admin account on the cluster. +You can generate this using `nix run '.#gen-k3s-cert' ~/.kube`, assuming you have SSH access to the master node. +This puts a private key, signed certificate and a kubeconfig in the kubeconfig directory ## Bootstrapping -We are now ready to deploy to the Kubernetes cluster. Deployments are done -through an experimental Kubernetes feature called -[ApplySets](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/#how-to-delete-objects). +We are now ready to deploy to the Kubernetes cluster. +Deployments are done through an experimental Kubernetes feature called [ApplySets](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/#how-to-delete-objects). Each applyset is responsible for a set number of resources within a namespace. -If the cluster has not been initialized yet, we must bootstrap it first. Run -these deployments: - +If the cluster has not been initialized yet, we must bootstrap it first. +Run these deployments: - `nix run '.#bootstrap-default-deploy'` - `nix run '.#bootstrap-kube-system-deploy'` ## Deployment -Now the cluster has been initialized and we can deploy applications. To explore -which applications we can deploy, run `nix flake show`. Then, for each -application, run `nix run '.#-deploy'`. Or, if you're lazy: -`nix flake show --json | jq -r '.packages."x86_64-linux"|keys[]' | grep -- -deploy | xargs -I{} nix run ".#{}"`. +Now the cluster has been initialized and we can deploy applications. +To explore which applications we can deploy, run `nix flake show`. +Then, for each application, run `nix run '.#-deploy'`. +Or, if you're lazy: `nix flake show --json | jq -r '.packages."x86_64-linux"|keys[]' | grep -- -deploy | xargs -I{} nix run ".#{}"`. diff --git a/applyset-deploy.sh b/applyset-deploy.sh index f7ec8e7..81bc8dc 100644 --- a/applyset-deploy.sh +++ b/applyset-deploy.sh @@ -2,31 +2,11 @@ set -euo pipefail -CREATE_LOCAL_GCROOT=false - -while [[ "$#" -gt 0 ]]; do - case "$1" in - --help) - echo "Use --create-local-gcroot to create local GC root" - exit 0 - ;; - --create-local-gcroot) - CREATE_LOCAL_GCROOT=true - shift - ;; - *) - echo "Unknown option: $1" - exit 1 - ;; - esac -done - first_server="${SERVERS%% *}" -previous_manifest=$( - envsubst <. - ''; - - example = lib.literalExpression '' - server = { - hosts = [ "0.0.0.0:5232" "[::]:5232" ]; - }; - auth = { - type = "htpasswd"; - htpasswd_filename = "/etc/radicale/users"; - htpasswd_encryption = "bcrypt"; - }; - storage = { - filesystem_folder = "/var/lib/radicale/collections"; - }; - ''; - }; - }; - - config = lib.mkIf cfg.enable (let - configFile = settingsFormat.generate "radicale.ini" cfg.settings; - in { - init.services.radicale = { - enabled = true; - user = lib.mkDefault "radicale"; - group = lib.mkDefault "radicale"; - - script = pkgs.writeShellScript "radicale-run" '' - ${cfg.package}/bin/radicale \ - --config ${configFile} - ''; - }; - - environment.systemPackages = [cfg.package]; - - users.users.${cfgInit.user} = nglib.mkDefaultRec { - description = "radicale"; - group = cfgInit.group; - createHome = false; - home = "/var/empty"; - useDefaultShell = true; - uid = config.ids.uids.radicale; - }; - - users.groups.${cfgInit.group} = nglib.mkDefaultRec {gid = config.ids.gids.radicale;}; - }); -} diff --git a/nixng-modules/sonarr.nix b/nixng-modules/sonarr.nix deleted file mode 100644 index b78a4cc..0000000 --- a/nixng-modules/sonarr.nix +++ /dev/null @@ -1,48 +0,0 @@ -{ - lib, - nglib, - config, - pkgs, - ... -}: let - cfg = config.services.sonarr; - cfgInit = config.init.services.sonarr; -in { - options.services.sonarr = { - enable = lib.mkEnableOption "sonarr"; - package = lib.mkPackageOption pkgs "sonarr" {}; - - dataDir = lib.mkOption { - type = lib.types.str; - default = "/var/lib/sonarr/.config/NzbDrone"; - description = "The directory where Sonarr stores its data files."; - }; - }; - - config = lib.mkIf cfg.enable { - init.services.sonarr = { - enabled = true; - user = lib.mkDefault "sonarr"; - group = lib.mkDefault "sonarr"; - tmpfiles = with nglib.nottmpfiles.dsl; [(e "${cfg.dataDir}/logs" "-" cfgInit.user cfgInit.group "7d" _)]; - - script = pkgs.writeShellScript "sonarr-run" '' - umask 0002 - ${lib.getExe cfg.package} -nobrowser -data=${cfg.dataDir} - ''; - }; - - environment.systemPackages = [cfg.package]; - - users.users.${cfgInit.user} = lib.mkIf (cfgInit.user == "sonarr") (nglib.mkDefaultRec { - description = "sonarr"; - inherit (cfgInit) group; - createHome = false; - home = "/var/empty"; - useDefaultShell = true; - uid = config.ids.uids.sonarr; - }); - - users.groups.${cfgInit.group} = lib.mkIf (cfgInit.group == "sonarr") (nglib.mkDefaultRec {gid = config.ids.gids.sonarr;}); - }; -} diff --git a/scripts/gen-k3s-cert.sh b/scripts/gen-k3s-cert.sh index 71ad441..405f9f9 100644 --- a/scripts/gen-k3s-cert.sh +++ b/scripts/gen-k3s-cert.sh @@ -13,11 +13,6 @@ if [ -z "$username" ] || [ -z "$host" ] exit 1 fi -if [ ! -d "$output_path" ]; then - echo "Output directory $output_path does not exist!" - exit 1 -fi - # Create a temporary directory temp=$(mktemp -d) diff --git a/secrets.yml b/secrets.yml index 8d8e5c1..170dd31 100644 --- a/secrets.yml +++ b/secrets.yml @@ -1,16 +1,11 @@ freshrss: password: ENC[AES256_GCM,data:ECDPrW+VgO8PY9p2fLIreRETNiRL5ZGnu/PMC7aNj8KaWfyNYL+l3w==,iv:srR/r1EtOpC/CKKrCDKcTLVdMFPAYIJIB1CCg8mS0UU=,tag:YN4PqR5uvPkVskpJWD+91g==,type:str] - oidc_crypto_key: ENC[AES256_GCM,data:dFQKZtFVd5l8W2go6WcK76o7O7hpQWnQKXCGTf9EhSVURvWigv6zzBULie7Y4lkJCsItG8oKmIiCYSy3MhFnU3DJTUJcenm4I7NHyINjvzHOBgUVPXbYQjQhouJwOlPkdqlSKv1f38ItZKNPJebMObZj+kACKbjdik6e6yM40RM=,iv:g6Ygval2qTQwKnrliI+n/r9OxJFePT9MKYyBLU6b3UQ=,tag:kWXTbm2JIR5aL/s4OX2Tqg==,type:str] pihole: password: ENC[AES256_GCM,data:MA60825Tl6aYEFVoPgo8k5Vjb9zmIxtPLJriQV1B3P1bOKu1KK7vxQ==,iv:RGZHox8CbJiEEEjMo2k/tNbtjCPy/QY7vOuMN/YNZcg=,tag:yphrq03IKpXM/tSDBLeSgA==,type:str] hedgedoc: databaseURL: ENC[AES256_GCM,data:6+IV4TaClIGE1XVkUf7JwXzqx3EvWiIKFx9X5x7QKvQKC7bIieD1ADVeAMQmiQfibnH/YV5TgjNY8Ft+3eX881c3yD+2j7mM+O1fX6taK/BCokDnqhIwTN2qxHsu+mrPcM/Pgg5Zqy8HvUgX8jM=,iv:bCwuNk5CVgK2T5IgLebcKwxwloi6FkWMWhnxwJek1GM=,tag:UDQ0KmRDVlDh35Fjm6eaAA==,type:str] sessionSecret: ENC[AES256_GCM,data:7FdRjAShjjue1fFwizCgK+94mkbT4ohAPxdyn/8Z8/f2nvGWPZHO/hGexOixbRGLPewJSaMunTMeJL+IzFlGlg==,iv:iz7640b8Mlb6mNps20b+TbphWDEFUbKwKNUXc0kR5NY=,tag:fdEr1tbes1h8VCA/q+0sOw==,type:str] databasePassword: ENC[AES256_GCM,data:wdRhCluNx2IzgDqouAoIcG6yOWwNLOaEkpqgYEeFvJDZsMC8OUuV7Q==,iv:Csut0c+LRKWD2b4uVuQpGnwwVqnGT6Sk6T/ODlH57Bk=,tag:7bS6a18GyRifq2D6cIheaw==,type:str] - oidc: - client_secret: - password: ENC[AES256_GCM,data:rLkMFkEzCF3+ejGAUliaBMpfOrxL5b3pJVvkblEFIvHupmr9DTS71L4T6/oo616E4IoCuZxqKh0FxhdWZRM4KrEk5YZMbgp9,iv:TbySrCTY+Kps4v/q5maQglm4aOzuUPch2ECBHPp4FYc=,tag:mz71m6JqIemVCypf93hkBQ==,type:str] - digest: ENC[AES256_GCM,data:m+abt7SEXZoeOA+ioB3BmiWN09CDpCWdZ5eNRhShvuUJW7rjbSJAQ2NTZkMTnFoNv6PO6rSUD6lmStFfO/mqVUFvWXBBDMK3xK+ILcgDoZZ1Pnmr19noqTL5OxyYLAtUHRNOohsUBYlZKyv2lFNQlqxk6kOi+fUPitmmsO6BO1nrq2s=,iv:CmC0JLXuT9O29Rtu7Pp8i0h/SyaEAT0bsXQb2LRFAkk=,tag:/IL39BCMuw+bqxv2OaNwhw==,type:str] nextcloud: databasePassword: ENC[AES256_GCM,data:jRLgW96FnMEpU0T5z/iQOX/CgjpH2ZykZGd1qGFHK8o=,iv:YrY9IsrlCaiQ8BFYqu+UnOxnvvB/JN4iYfy3vMa3wcw=,tag:41iWc4iVqjdUr02O5CLu7g==,type:str] paperless: @@ -34,29 +29,6 @@ immich: tailscale: clientID: ENC[AES256_GCM,data:O8tTyy55xP85JkbJNR5daB4=,iv:SMj83Sxh7BvPRG3l5TnnpmclO5N2treUQCCJuMy8cO8=,tag:UUSN3bsZvb09cyYN65RQDg==,type:str] clientSecret: ENC[AES256_GCM,data:c8E/a7McI+wGN9TFJ/yzTSkrhUlISmrNJdjDDMqAQrZ8s5wFEZ+4+h+dtwcjF9Ykj198glgny7cP3HubHVDw,iv:ifaP4NmLRQbYQtJQaMMCMaehosapZ2R3im9ew5h6f9E=,tag:XF+xB94nua8RZlkGxFDFFQ==,type:str] -authentik: - secret_key: ENC[AES256_GCM,data:bbEEpymADAGY/fDNMU7FfzveyK2SBUBCitQLN85tB5C5u32PRsRnOa2MDjKGU4kArnyV/WtJQXT4HJ//nMLVh53Q8BYclWYYVFourEjaajTixkZ2gkAfcMJ1mMaQG09ylrwMhLsZWbeaLFzW6dfPSQOCchvj3VhgvJSXuhNmr90=,iv:aE/DGt/yd9wL5qlWfdyT/9SIsCj7U3GcljArcGIdh9k=,tag:CVeiZITOJ71/jdLzjZjteA==,type:str] - postgresql_password: ENC[AES256_GCM,data:4okPqDzPDnx7ZBFQV2Jtk6SEHTskRd2GVG4XLdpMQrgivKsuhQJf1QAnCWHrjmtg74xdlUy2TdPwTWGd5UiM1G90GwHSzLPSJt6X0IFMxCuq/eyYYbD9w8Wk1pVuvqoluPcjN6WoRJdCzap1QITih8B+oSkTJ/rk84xczsjah4U=,iv:r0dYWcsIqdH8FGuBd2dxAJ1AjRmk6k4QYKq0cnZITk4=,tag:errORaXgO7yJW7ERbmdtRg==,type:str] - oauth2: - freshrss: - client_secret: ENC[AES256_GCM,data:e7wdwWRS8KivGkcWaMgSrUEEuOTHzj1oim+qUcLD35/DA/V6itM2XqVPhqIOXHrf6pOyYgprEv14bEx8zUvtT6iXV4fsEUEWeWTgt4NI3YULtx/t0yVDq9Zc8fN9cIqGxGeig1mcQwmm7vByq58mNJEpcfz46swjN2ATf/CPJQs=,iv:xeNgSKd+g4ne8NLw/2KQjTXSvNkqezOhMn5niuWpD38=,tag:ElOUMg0oZ+q15hCgh/Mzug==,type:str] - hedgedoc: - client_secret: ENC[AES256_GCM,data:hdNQzatO6Pf6mxvfO4h1XrhycKMBUHElEwacGttzByi4JDbIndAwYc2GXdwUmytPMYs/s+lVjcdHhspUFWS01DETWQfnWm/GN73GzW18uj3XyRXqt62HhMf18GvRlOWkGX+jYpUTGGoonYes2xijhD/mNCjxKk5Q+6FVFT2mdJ4=,iv:pScEX6YnoU7HelxmCes8A9vJjPdvFbqbclHYMme8OOE=,tag:FURxphI8IDMvOwB4ahD8hg==,type:str] - paperless-ngx: - client_secret: ENC[AES256_GCM,data:GgF+gQt8olzKUzGMDL6mh6UWDv49OPDH5tB/gboWkFd7Njc1SrSkqf71gQryOcPQ0vpXrh0nK1z6ZjMpmDEA5ohTwWymeLCgwNtJSAMHZ1VlZ2aQZr70r3KtAxKjmTiT5flUYnxS79fCF43BveSMGeAshRCvQmYCdi43sP2E4To=,iv:DzsIRPiMzxaqVrjaHMVKWgOR0asZQzWf8EE1nxRSJmk=,tag:79bo7EzVq9tvL6ap6jfV+Q==,type:str] - forgejo: - client_secret: ENC[AES256_GCM,data:I0LBIrsPuARFEcvu0sKhIbkEYxLhZrwpRfPls3KDARu5rnfwgbJ6AVtfMmcAIM9ISFzXykoyMXossHo1i23N90PsHdl2t580EffhJ+q/UUfCIk7/rX/6CXlcb8WHdab4ymN5r9jEsgD3mAWX55IehU96ZKGRKRhxSIowCIYRhyQ=,iv:1wQDGCDhSu0s+IqXULiHmRiKGTLRvOjwsYaNMCWfkjg=,tag:p1mwks0KP9lhbciTIv3/Dw==,type:str] - immich: - client_secret: ENC[AES256_GCM,data:KrsaLLsjfQsyNQzvQF/pCLj1dhi8tr/OdToY7WczvPUUQKMtSk//oxsiPike/HoVEuCUp+j7UlTfIRPF2xUcPPvw7pkcLhQhcot79aieI1ciIeLZ1Q5svsPrqDBmDY7g65jkzA9vjM9VLTsx4Dx/1vGHDqo7I12qadEQlKAuhhQ=,iv:3icAM7sVe2HlmosbP7VPbcF4SRz/mlbzdQ1gENR9TRs=,tag:O8TCN7NltNpDGoG3T8Ds1w==,type:str] - nextcloud: - client_secret: ENC[AES256_GCM,data:zLejYbfudK/4OquLXPYTv9YOmFpCVfg0KLNkDSDCpFrxroDUAXBCLtYXiGuYkYrD/t7LAzRt+OTq70d7ciuHhBNSLclP2U97BQoXCWscWnxQauRZ+UCABvP+DB9VPQmCwU+uKPrKQ8l51baj+MkpIDdk2lwavpONMU57Zov6N2o=,iv:aQ4bsXUXn177tCxe1kAsSMP9ynEzvDwN0hwFhrT3Nko=,tag:EFcnf6VmyFt2i4+aL56sWw==,type:str] - kitchenowl: - client_secret: ENC[AES256_GCM,data:x4Xsd3d3El59HKBYNV56ah314hYSRhzt46upW34cOopXNHSB3zCDrD46LUa6i8g6V5GJyrMpMfO5mv+b80JrmfHkhGUXZXuTwDNu6ijnO6ZCvC2Bdlo+T0tlkJe25OMCBseJkkC++UBrpKQQTAhyVjnPSVrGVvtY4WtdAw+X/OY=,iv:pOowIhPD7kb2F3ylFzLwNW3BhPZyzoFCGRm2+KCmhno=,tag:GxFI0w06EyGxFwj6Fv4ZLQ==,type:str] - mealie: - client_secret: ENC[AES256_GCM,data:VNEV8a1KZc6XVeRzyBWzuwldTmxEepPRUOEMEM3HKrDIkxcGHDuoLh5P7Ti+jS5rbmua+ET4GPcJTYXR+pO5/cMaxqFONj1D1w9541QPYZNBbTfPM/Zfu8OnzngVsCnnKEtu1bVwflUnmf7F5hHED8zJRe1F9PT/HYA6NCd4ajQ=,iv:58ysTItP8UNnQWwgWRS1dk/K/2dJv3P5wa5rGnz2P/I=,tag:vLGrFldzOey9ANW010GylA==,type:str] -smtp2go: - username: ENC[AES256_GCM,data:BEr7Rq7rlGvfYEpY/ZXnhM2eClnHdqU81A==,iv:dwYD5h+C5bzS9ikUgxQ51+jRQ32TtDy2PhDbd1tpS8Q=,tag:CjjLDz5n4H28qi8jWf9S4w==,type:str] - password: ENC[AES256_GCM,data:Yys6qy6DRYo16+X+Uj9oa9otjaKBnHOtIQ==,iv:G7H9mxsODShFoVlNMwuV8O18NBG/7LTFDFdqnH83YkE=,tag:hSlYp27QMoPZwiKBqyOpKA==,type:str] sops: kms: [] gcp_kms: [] @@ -81,8 +53,8 @@ sops: azR0UkJyL0RwUVk4ZzdkSWptcDlWVjAK5FU9B5TBSnV3azO4eCv13T6i3dGGuI68 UgBrVEb1/Fv+4XTjeSEhpiOaH8sNWYoNa3Aa7uTZYlHDRWga2GC7zw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-02-15T15:37:53Z" - mac: ENC[AES256_GCM,data:tsoDYbuhxEH3PrxOPgfKczD8Hh1XGJRhGAtm2DWpPP9T99ub/l3KAV2pInvUi5Kn+1QvhJUAwFAP6A/435cqfsHxQI066N7ADUYO4qshcsAYKK7ofBVNnI431D3oD+kBujWKmvSqhlamdP+O7O1ICtbfI5PEM8SN5KWEvEtyp9A=,iv:pDiPy6EWLaZQbNydRFTktRlcf7M9Uf8OS+WPbQkUx9M=,tag:D+tMTFVbWE7TQIw/0MUZjw==,type:str] + lastmodified: "2024-12-01T13:22:41Z" + mac: ENC[AES256_GCM,data:6UqmxHJC4KWsiQttXFEEG1opPcrGntYj9nlD8m0iBqjc9g/SHxEogpaiYEnriGNXGw0HhRWjrd+JX29Ht4xVeiYqthYX+4rVuIuv+SI7p08hJeIBbIYrfonAJsebbSsynuy9YgyUkNZhoqjZTtuzFU/c4Dh5453RVnuQmu4PZNs=,iv:yA//mqJ0Ft63eRME8A1HBiZ/B0gcVYlS4MaP0LykooU=,tag:0NxU0lVi67N34eDhsT82kQ==,type:str] pgp: [] unencrypted_suffix: _unencrypted - version: 3.9.4 + version: 3.9.1