diff --git a/modules/config/instances/config/torrent.nix b/modules/config/instances/config/torrent.nix index 61ff528..ac75a67 100755 --- a/modules/config/instances/config/torrent.nix +++ b/modules/config/instances/config/torrent.nix @@ -7,10 +7,10 @@ let mntPath secretPath ; - label = "qBittorrent"; - name = "qbittorrent"; - short = "share"; - domain = "${short}.${domain0}"; + label = "Rqbit"; + name = "rqbit"; + subdomain = "share"; + domain = "${subdomain}.${domain0}"; secrets = "${secretPath}/${name}"; ssl = "${sslPath}/${domain}"; in @@ -21,9 +21,10 @@ in domains = { url0 = domain; }; - subdomain = short; + subdomain = name; tags = [ name + "rqbit" "torrent" "p2p" ]; @@ -31,9 +32,9 @@ in port0 = 3030; }; interface = { - id = "vm-${short}"; + id = "vm-${name}"; mac = "02:00:00:00:56:07"; - idUser = "vmuser-${short}"; + idUser = "vmuser-${name}"; macUser = "02:00:00:00:00:07"; ip = "192.168.50.117"; gate = "192.168.50.1"; @@ -45,7 +46,7 @@ in key = "${ssl}/key.pem"; }; varPaths = { - path0 = "${varPath}/${label}"; + path0 = "${varPath}/${name}"; }; mntPaths = { path0 = "${mntPath}/${name}"; diff --git a/modules/nixos/guests/jellyfin/default.nix b/modules/nixos/guests/jellyfin/default.nix index 5115be3..da9c2ae 100755 --- a/modules/nixos/guests/jellyfin/default.nix +++ b/modules/nixos/guests/jellyfin/default.nix @@ -83,7 +83,7 @@ in microvm = { vcpu = 1; - mem = 1024 * 3; + mem = 1024; hypervisor = "qemu"; interfaces = [ { diff --git a/modules/nixos/guests/mastodon/default.nix b/modules/nixos/guests/mastodon/default.nix index be0a8c9..69ad463 100755 --- a/modules/nixos/guests/mastodon/default.nix +++ b/modules/nixos/guests/mastodon/default.nix @@ -397,7 +397,7 @@ in environment.systemPackages = [ fedifetcherPython ]; microvm = { - vcpu = 4; + vcpu = 1; mem = 1024 * 6; hypervisor = "qemu"; interfaces = [ diff --git a/modules/nixos/guests/torrent/default.nix b/modules/nixos/guests/torrent/default.nix index ffd1a4c..1663ecb 100755 --- a/modules/nixos/guests/torrent/default.nix +++ b/modules/nixos/guests/torrent/default.nix @@ -1,7 +1,6 @@ { config, flake, - pkgs, ... }: let @@ -11,94 +10,30 @@ let host = instances.torrent.domains.url0; dns0 = instances.web.dns.provider0; dns0Path = "dns/${dns0}"; - torrentPort = 2049; + in { + microvm.vms.${serviceCfg.name} = { autostart = true; config = { system.stateVersion = "25.05"; - networking = { - wg-quick.interfaces = { - wg0 = { - address = [ - "172.22.116.126/32" - "fd00:4956:504e:ffff::ac16:747e/128" - ]; - dns = [ "172.16.0.1" ]; - privateKeyFile = "/run/secrets/wireguard-pass"; - peers = [ - { - publicKey = "hku9gjamhoii8OvxZgx+TdUDIkOAQYFu39qbav2AyUQ="; - endpoint = "89.187.181.116:${builtins.toString torrentPort}"; - allowedIPs = [ - "0.0.0.0/0" - "::/0" - ]; - persistentKeepalive = 25; - } - ]; - }; - }; - firewall = { - enable = true; - allowedTCPPorts = [ - 22 - torrentPort - serviceCfg.ports.port0 - ]; - allowedUDPPorts = [ - torrentPort - ]; - }; - dhcpcd.enable = false; - useNetworkd = true; - }; - - # imports = [ - # ./rqbit.nix - # ]; + networking.firewall.allowedTCPPorts = [ + 22 + serviceCfg.ports.port0 + ]; + imports = [ + ./rqbit.nix + ]; services = { - qbittorrent = { + rqbit = { enable = true; - webuiPort = serviceCfg.ports.port0; - torrentingPort = torrentPort; + dataDir = "/var/lib/rqbit/downloads"; + listenAddress = "0.0.0.0"; openFirewall = true; - serverConfig = { - LegalNotice.Accepted = true; - - BitTorrent = { - Session = { - Interface = "wg0"; - InterfaceName = "wg0"; - MaxConnections = -1; - Port = torrentPort; - MaxConnectionsPerTorrent = -1; - MaxUploads = -1; - MaxUploadsPerTorrent = -1; - }; - }; - - Preferences = { - WebUI = { - Username = "user"; - # generate new passwords with this: - # https://codeberg.org/feathecutie/qbittorrent_password - Password_PBKDF2 = "@ByteArray(1bJKXLVSLU6kgCHbCS2lDg==:BmyrMaod6dbJqEe7Ud/JgKAxRMqzsAuEjHcTvLzIBgc5rc5Z7J2X9mbH0cDEAhXqc+O3gQxrckt8S2Gf+zlO9w==)"; - }; - - General = { - Locale = "en"; - }; - Downloads = { - SavePath = "${serviceCfg.varPaths.path0}/downloads"; - TempPathEnabled = false; - PreAllocation = false; - }; - }; - }; + extraArgs = [ ]; }; openssh = { @@ -113,28 +48,21 @@ in network = { enable = true; networks."10-enp" = { - matchConfig.Name = "enp0s5"; + matchConfig.Name = "enp0s4"; addresses = [ { Address = "${serviceCfg.interface.ip}/24"; } ]; gateway = [ serviceCfg.interface.gate ]; }; }; - tmpfiles.rules = [ "d ${serviceCfg.varPaths.path0} 755 ${serviceCfg.name} ${serviceCfg.name} -" "d ${serviceCfg.varPaths.path0}/downloads 755 ${serviceCfg.name} ${serviceCfg.name} -" ]; - - services.qbittorrent = { - after = [ "wg-quick-wg0.service" ]; - requires = [ "wg-quick-wg0.service" ]; - }; }; microvm = { - vcpu = 1; - mem = 1024 * 1; + vcpu = 4; + mem = 1024 * 4; hypervisor = "qemu"; - interfaces = [ { type = "tap"; @@ -147,7 +75,6 @@ in mac = serviceCfg.interface.macUser; } ]; - forwardPorts = [ { from = "host"; @@ -164,24 +91,20 @@ in proto = "virtiofs"; } { - mountPoint = serviceCfg.varPaths.path0; + mountPoint = "/var/lib/${serviceCfg.name}"; proto = "virtiofs"; source = serviceCfg.mntPaths.path0; tag = "${serviceCfg.name}_data"; } - { - mountPoint = "/run/secrets"; - proto = "virtiofs"; - source = "/run/secrets/proton"; - tag = "host_secrets"; - } + # { + # mountPoint = "/run/secrets"; + # proto = "virtiofs"; + # source = "/run/secrets/${serviceCfg.name}"; + # tag = "host_secrets"; + # } ]; }; - - environment.systemPackages = with pkgs; [ - wireguard-tools - speedtest-go - ]; + environment.systemPackages = [ ]; }; }; @@ -193,6 +116,7 @@ in basic_auth { {$CADDY_AUTH_USER} {$CADDY_AUTH_PASSWORD_HASH} } + reverse_proxy ${serviceCfg.interface.ip}:${toString serviceCfg.ports.port0} tls ${serviceCfg.ssl.cert} ${serviceCfg.ssl.key} @@ -205,15 +129,11 @@ in }; sops.secrets = { - "caddy/share-auth" = { + "caddy/${serviceCfg.name}-auth" = { owner = "caddy"; group = "caddy"; mode = "0400"; }; - "proton/wireguard-pass" = { - owner = "root"; - mode = "0400"; - }; }; security.acme.certs."${host}" = { @@ -227,13 +147,15 @@ in systemd = { services.caddy = { serviceConfig = { - EnvironmentFile = config.sops.secrets."caddy/share-auth".path; + EnvironmentFile = config.sops.secrets."caddy/${serviceCfg.name}-auth".path; }; }; + tmpfiles.rules = [ "d ${serviceCfg.mntPaths.path0} 0755 microvm wheel - -" "d ${serviceCfg.secretPaths.path0}/caddy 755 caddy caddy -" "d /var/log/caddy 755 caddy caddy -" + ]; }; } diff --git a/modules/nixos/guests/torrent/rqbit.nix b/modules/nixos/guests/torrent/rqbit.nix index 897e8bb..d1fbd8b 100755 --- a/modules/nixos/guests/torrent/rqbit.nix +++ b/modules/nixos/guests/torrent/rqbit.nix @@ -4,7 +4,9 @@ pkgs, ... }: + with lib; + let cfg = config.services.rqbit; in @@ -25,221 +27,24 @@ in description = "Directory to store downloaded torrents."; }; - # HTTP API Configuration - httpApi = { - listenAddress = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "IP address to listen on for the web UI and API."; - }; - - listenPort = mkOption { - type = types.port; - default = 3030; - description = "Port for the web UI and API."; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = "Open the firewall for the web UI port."; - }; + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "IP address to listen on for the web UI and API."; }; - # BitTorrent TCP Configuration - tcp = { - minPort = mkOption { - type = types.port; - default = 4240; - description = "Minimum port for incoming BitTorrent connections."; - }; - - maxPort = mkOption { - type = types.port; - default = 4260; - description = "Maximum port for incoming BitTorrent connections."; - }; - - disable = mkOption { - type = types.bool; - default = false; - description = "Disable listening for incoming TCP connections."; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = "Open firewall ports for incoming BitTorrent connections."; - }; + listenPort = mkOption { + type = types.port; + default = 3030; + description = "Port for the web UI and API."; }; - # DHT Configuration - dht = { - disable = mkOption { - type = types.bool; - default = false; - description = "Disable DHT (Distributed Hash Table) for peer discovery."; - }; - - disablePersistence = mkOption { - type = types.bool; - default = false; - description = "Disable DHT state persistence (useful for multiple instances)."; - }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open the firewall for the web UI port."; }; - # UPnP Configuration - upnp = { - disablePortForward = mkOption { - type = types.bool; - default = false; - description = "Disable UPnP port forwarding."; - }; - - enableServer = mkOption { - type = types.bool; - default = false; - description = "Enable UPnP Media Server to stream torrents."; - }; - - serverFriendlyName = mkOption { - type = types.nullOr types.str; - default = null; - description = "Friendly name for the UPnP server."; - example = "rqbit Media Server"; - }; - }; - - # Rate Limiting - rateLimit = { - download = mkOption { - type = types.nullOr types.int; - default = null; - description = "Download rate limit in bytes per second."; - example = 1048576; # 1 MB/s - }; - - upload = mkOption { - type = types.nullOr types.int; - default = null; - description = "Upload rate limit in bytes per second."; - example = 524288; # 512 KB/s - }; - }; - - # Logging Configuration - logging = { - level = mkOption { - type = types.nullOr ( - types.enum [ - "trace" - "debug" - "info" - "warn" - "error" - ] - ); - default = null; - description = "Console log level."; - }; - - file = mkOption { - type = types.nullOr types.path; - default = null; - description = "Log file path (in addition to console logging)."; - example = "/var/log/rqbit/rqbit.log"; - }; - - fileRustLog = mkOption { - type = types.str; - default = "librqbit=debug,info"; - description = "RUST_LOG value for the log file."; - }; - }; - - # Performance Configuration - performance = { - workerThreads = mkOption { - type = types.nullOr types.ints.positive; - default = null; - description = "Number of worker threads for the executor."; - }; - - maxBlockingThreads = mkOption { - type = types.ints.positive; - default = 8; - description = "Maximum blocking threads for disk I/O operations."; - }; - - singleThreadRuntime = mkOption { - type = types.bool; - default = false; - description = "Use tokio's single-threaded runtime (for debugging)."; - }; - - concurrentInitLimit = mkOption { - type = types.ints.positive; - default = 5; - description = "Maximum number of torrents that can initialize simultaneously."; - }; - }; - - # Peer Configuration - peer = { - connectTimeout = mkOption { - type = types.str; - default = "2s"; - description = "Peer connection timeout."; - example = "1.5s"; - }; - - readWriteTimeout = mkOption { - type = types.str; - default = "10s"; - description = "Peer read/write timeout."; - example = "5s"; - }; - }; - - # Tracker Configuration - tracker = { - refreshInterval = mkOption { - type = types.nullOr types.str; - default = null; - description = "Force a specific tracker refresh interval."; - example = "30s"; - }; - - trackersFile = mkOption { - type = types.nullOr types.path; - default = null; - description = "File with tracker URLs to use for all torrents."; - }; - }; - - # Advanced Options - socksProxy = mkOption { - type = types.nullOr types.str; - default = null; - description = "SOCKS5 proxy URL."; - example = "socks5://user:pass@localhost:1080"; - }; - - blocklistUrl = mkOption { - type = types.nullOr types.str; - default = null; - description = "URL to download a P2P blocklist from."; - example = "https://example.com/blocklist.txt"; - }; - - umask = mkOption { - type = types.nullOr types.str; - default = null; - description = "Set the process umask for file creation permissions."; - example = "022"; - }; - - # User/Group Configuration user = mkOption { type = types.str; default = "rqbit"; @@ -256,7 +61,7 @@ in type = types.listOf types.str; default = [ ]; description = "Extra command-line arguments to pass to rqbit."; - example = literalExpression ''[ "--experimental-mmap-storage" ]''; + example = literalExpression ''[ "--upnp" "--enable-upnp-server" ]''; }; }; @@ -266,73 +71,29 @@ in after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - preStart = mkIf (cfg.logging.file != null) '' - mkdir -p $(dirname ${cfg.logging.file}) - chown ${cfg.user}:${cfg.group} $(dirname ${cfg.logging.file}) - ''; - serviceConfig = { Type = "simple"; User = cfg.user; Group = cfg.group; + Environment = [ - "XDG_CACHE_HOME=${cfg.dataDir}/.cache" - "XDG_DATA_HOME=${cfg.dataDir}/.local/share" + "XDG_CACHE_HOME=/var/lib/rqbit/.cache" + "XDG_DATA_HOME=/var/lib/rqbit/.local/share" ]; - ExecStart = - let - args = [ - "${cfg.package}/bin/rqbit" - "--http-api-listen-addr ${cfg.httpApi.listenAddress}:${toString cfg.httpApi.listenPort}" - ] - ++ optional (cfg.logging.level != null) "-v ${cfg.logging.level}" - ++ optional (cfg.logging.file != null) "--log-file ${cfg.logging.file}" - ++ optional (cfg.logging.file != null) "--log-file-rust-log ${cfg.logging.fileRustLog}" - ++ optional (cfg.tracker.refreshInterval != null) "-i ${cfg.tracker.refreshInterval}" - ++ optional cfg.performance.singleThreadRuntime "-s" - ++ optional cfg.dht.disable "--disable-dht" - ++ optional cfg.dht.disablePersistence "--disable-dht-persistence" - ++ optional (cfg.peer.connectTimeout != "2s") "--peer-connect-timeout ${cfg.peer.connectTimeout}" - ++ optional ( - cfg.peer.readWriteTimeout != "10s" - ) "--peer-read-write-timeout ${cfg.peer.readWriteTimeout}" - ++ optional (cfg.performance.workerThreads != null) "-t ${toString cfg.performance.workerThreads}" - ++ optional cfg.tcp.disable "--disable-tcp-listen" - ++ optional (cfg.tcp.minPort != 4240) "--tcp-min-port ${toString cfg.tcp.minPort}" - ++ optional (cfg.tcp.maxPort != 4260) "--tcp-max-port ${toString cfg.tcp.maxPort}" - ++ optional cfg.upnp.disablePortForward "--disable-upnp-port-forward" - ++ optional cfg.upnp.enableServer "--enable-upnp-server" - ++ optional ( - cfg.upnp.serverFriendlyName != null - ) "--upnp-server-friendly-name '${cfg.upnp.serverFriendlyName}'" - ++ optional ( - cfg.performance.maxBlockingThreads != 8 - ) "--max-blocking-threads ${toString cfg.performance.maxBlockingThreads}" - ++ optional (cfg.socksProxy != null) "--socks-url ${cfg.socksProxy}" - ++ optional ( - cfg.performance.concurrentInitLimit != 5 - ) "--concurrent-init-limit ${toString cfg.performance.concurrentInitLimit}" - ++ optional (cfg.umask != null) "--umask ${cfg.umask}" - ++ optional ( - cfg.rateLimit.download != null - ) "--ratelimit-download ${toString cfg.rateLimit.download}" - ++ optional (cfg.rateLimit.upload != null) "--ratelimit-upload ${toString cfg.rateLimit.upload}" - ++ optional (cfg.blocklistUrl != null) "--blocklist-url ${cfg.blocklistUrl}" - ++ optional (cfg.tracker.trackersFile != null) "--trackers-filename ${cfg.tracker.trackersFile}" - ++ cfg.extraArgs - ++ [ - "server" - "start" - cfg.dataDir - ]; - in - concatStringsSep " " args; + + ExecStart = '' + ${cfg.package}/bin/rqbit \ + --http-api-listen-addr ${cfg.listenAddress}:${toString cfg.listenPort} \ + ${concatStringsSep " " cfg.extraArgs} \ + server start ${cfg.dataDir} + ''; Restart = "on-failure"; + StateDirectory = "rqbit"; NoNewPrivileges = true; PrivateTmp = true; ProtectSystem = "strict"; - ReadWritePaths = [ cfg.dataDir ] ++ optional (cfg.logging.file != null) (dirOf cfg.logging.file); + ReadWritePaths = [ cfg.dataDir ]; }; }; @@ -348,12 +109,8 @@ in rqbit = { }; }; - networking.firewall = mkIf (cfg.httpApi.openFirewall || cfg.tcp.openFirewall) { - allowedTCPPorts = optional cfg.httpApi.openFirewall cfg.httpApi.listenPort; - allowedTCPPortRanges = optional cfg.tcp.openFirewall { - from = cfg.tcp.minPort; - to = cfg.tcp.maxPort; - }; + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.listenPort ]; }; }; } diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml index dc90627..9ca0160 100755 --- a/secrets/secrets.yaml +++ b/secrets/secrets.yaml @@ -47,15 +47,13 @@ glance-jellyfin: ENC[AES256_GCM,data:ozdDKgAWkA88J2j8RtiOP/aQPAt/neUOSlAZF20g510 opencloud: projectenv: ENC[AES256_GCM,data:+XCd3xScfxCN1Zl5L+4RAOjpmMPhVLSBtqH2nkEUpXhssy5EU82qAanNmqwiIJ1VrYXYovuu3XOwRKY3Ub1nsR5h1S0KUCwav2zmFKVopxF/5jVNIk6qR8Ggz/fAa1YQSW+SAnrtRGvP0Q1SERlCgnH4isVxNvWPyWCZKIgiX2Enu7hVwsJXKLYDomRWt47zzXNUzw50aFn7xPtXE/AYbMPBa+FweCrCfkaQ6i6jPvkdc6VBYTqIanD0908wB2SJA+1xvY7bYgRVB17/4a/9DuUN5J4xU84TOW7EFkvC/hWhlhC58GqQrOFyAgTP4YJHKGbLVKPlc4fcNMh5+pENpPG2fRDElCaLoJcYe6sYhaCDSegpDR/U9bgzKirnCu/hmdG+NQ3sGK/C89JL2kZT+tVT1u5JWnKGOGvLGQm73QUmnssDZVd8ubNsnd57W7siqAXY3+DN46yLrGgmTfHTRi4x2DKF8VCD9jXOxWsyoLvKYDyz09H9dI72xlCtSmcrFAt7bY7uEAWutrPCf3Kh/gq6oFUAPBEwfqhgnpgGA1vyA6o4zhxl4Rqye5YZMx2uNkxdA4wmk9KB/e7BVR/P04TSXoAV931OX7bnlw3XjSw5NTPEPnpmwZ3VPRGGkz171RiQQp+CkwUr35+DdwFrGazuv3wlwAhM19h9SRn8jikrw6PPGVehYp8mB/FhpNgqV0nM2DfjaBqE3yMfDzXH5b92t4Q=,iv:6mlHq6yh03x/FbZNu+A9QBoV6ALX1rRWuL13ItJWriI=,tag:tK6Ek2fzgPPWT8WCeU1Frw==,type:str] caddy: - share-auth: ENC[AES256_GCM,data:3jY2B2GOdz5EPJeAyVsk4XCs5NMft3VquIBep7SxYtEZ9H7IDroq1U1Sch6YVQ7VcL85L4Ix/OVPm4jVDEA0sZiGkltbYXRXZ8CR34ifsHtHR35lgjXyj8ZhJLydw7LgmZCEztWO8GjLdvSY,iv:MT5sA32Djx81HGc36rqV2xS5KUHLAeTyZiOdSu8oqQY=,tag:V1dv4yS2RXf4Xqrl5+tEuA==,type:str] + rqbit-auth: ENC[AES256_GCM,data:4fLeeykasmPDtUC5wFRqWEVxRa4ntaiDU2v3AUBdUWxiRQ9EDEk3S1lWpwBtMlan3bU/QsIGVIh/hhWmWD2WdkcaKJxvPGtb9OSJ3wtxCTlI2EsWS1h3BQY4Z1Jf5VVDLR+/dj3mZNv7cu1D,iv:+fIzvbXsE7EoSrfsD1ZzKMmF1BazHre7yXQbjyKdDks=,tag:d/wFZf8szahaU9LqM7lr/g==,type:str] comfyui-auth: ENC[AES256_GCM,data:7VTXoRxnD0NyVCFRAjHaZswEUsFuQd/ZIwVfqGPmNNV87hn6CBYWvxvcPPFwe+uw7BmKMt+I66DyKx5ydYENTWxPocyT/rFdgdtWwNoenj+JwsUzegmMbEiH2HCZdiwKj0h1lo142mtA6zkc,iv:xT5XHCj8D4dyvglstE2oqo92fLdscCkaNMux43hJ7nQ=,tag:HgU9wAmjPvfoDXgnorB5yA==,type:str] wifi-home: ENC[AES256_GCM,data:5NYSCUyalDf7gZF7WaRQJCo=,iv:RkVZKsmVEBg5M28DSkBD41673iLM+dqDAAhSwjqejck=,tag:QQ17VSWOnU0bGglZq6455Q==,type:str] firefly-iii: pass: ENC[AES256_GCM,data:WjHcoTuEzEq9pfw4QoqRjI4jhu5VPEMOXlHL0olg9dqUj4EGa1Shv5T/kIxdRFuao0y3zQ==,iv:4/fmFOxxDLzplsNGpSJMQOeoNviZw2c2pFlB1ZkRu+o=,tag:7TQ2q/kEFDU4tZxPx53ebw==,type:str] data: ENC[AES256_GCM,data:921LhcRTWVk24eEAQoDMV+RllSP3PbSXCCIDXlQA80Mq,iv:YXEgas77DgdyPTnBZa/ySjcERBIwmdDZJbijeNKNF24=,tag:Wj25wA7tLJ2bZ/faG9DUhg==,type:str] smtp: ENC[AES256_GCM,data:+e4MiRZ2WOZyWYpMf+By1Eb45ih4TA+svLI2+00yQk82,iv:+52+kJouMwkOSDEaOCA8V80+wT/VzNxgtCkOO68SCdk=,tag:YrtrJAXIhQpsUTEeYvrVwQ==,type:str] -proton: - wireguard-pass: ENC[AES256_GCM,data:2SuLDMkJsOa/EjoYq0ZYp8A6yAO9KSaMJ35ofMXQDWhPcpL7/2eld83by021,iv:g5zGOdl2pGu3rsQcnwgRCHqDdzYNVkNhhfl3YMieBUY=,tag:FzbkDA4FyHrcC09EFZDoKw==,type:str] backblaze: env: ENC[AES256_GCM,data:cdOYt77KocuGB3aqYz13oBokoLkEIgI1AW+cYC5uutgZYujG3PqoLEh6Gvbpzn3O+0OWg1/4UAYr4f2v7oCsgwFzPWS3HrhqC5+kIBjrPCyAnxDxlu2xaQ9hR+ogFh5UTDo=,iv:6+jx4Dj5CNV72DAss6NNYm44f9gSHco/EUBvL2o2CNI=,tag:6/cx84MgTDqQJxu/zINEeA==,type:str] repo: ENC[AES256_GCM,data:sRae9XELIfkWPaXelCdgEXIDbLTHVqGcRO0o+WA9aBfB8MUw92JjRCYgMgGXT0Apy38eszyuEHFB3XPpRmtQ7g==,iv:EilVA9zdHm6B9pTIhNxyj6Th1248nXvh0kpnEqZJ5HI=,tag:q9ASAgx5vgY0IePws4rT5Q==,type:str] @@ -72,7 +70,7 @@ sops: bXBOa1VSakoyaWxpODJEOU11QUZCaUEK8Ch9Ten3DdrPHF1DTH2qei85AlHUOaLD aNfzakake7ej+MxJYdKEU0bcWofNMKzIlZa2uM10KZSENDP8d8qlig== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-11-26T08:19:12Z" - mac: ENC[AES256_GCM,data:eqsiEBdTva+rjC64+n/RhVqWT7q4QUfARAvqZCezVCAvoTFJ+qJO2PWrJ39H/axLcLTwq7xC36UYkN8ckvAUSLx8pZyyuHVeI+4irL16AIpJM8z14kYG6dyw0ujVI+JZpM57FHWXkwX6JtzbqN7sv+u/YnM6ubfmporykvlQOmQ=,iv:eYhCL11iv3B6jsGieJ7nosKcd6MUH1QBWjJptgjBhiQ=,tag:7W+bhwOHM7e0zCP7eP0jxQ==,type:str] + lastmodified: "2025-11-25T10:47:46Z" + mac: ENC[AES256_GCM,data:JF264gi1QgOcPk9GVzFeWrPa9T5AuHxuYQTVLbpSoGnqJEwQCvmsYKe1guqlNRlxm11VDGSa3DB1++0fpd4Vn7ngTxQuLCkCbnBo3fU+SDxe3gYt4c21pT3qabbcIuCGTqf6fy32ZWi8BabcFN1hN+LWuSox40PugnuU5SgZdXs=,iv:dbQb4OITPZozKyprRUV9df22zu+XwAr2S28K4A4Ki5I=,tag:alTMCTyKq/Cxn21h06kkGQ==,type:str] unencrypted_suffix: _unencrypted version: 3.11.0