feat: add additional security by migrating secrets from plaintext in nix-store
All checks were successful
ci / treefmt (push) Successful in 2m7s

This commit is contained in:
Ceferino Patino 2026-06-29 22:07:34 -05:00
commit bb9e5d34f6
Signed by: c4patino
SSH key fingerprint: SHA256:Wu+cU1t+7zVT9wzew/4meNmeulo66NzMqc22MdDBgXI
33 changed files with 254 additions and 193 deletions

View file

@ -62,3 +62,12 @@ creation_rules:
- age: - age:
- *master - *master
- *c4patino_mutualofomaha - *c4patino_mutualofomaha
- path_regex: secrets/sops/munge.key
key_groups:
- age:
- *master
- *c4patino_arisu
- *c4patino_kokoro
- *c4patino_chibi
- *c4patino_shiori
- *c4patino_tsuki

8
flake.lock generated
View file

@ -164,11 +164,11 @@
"dotfiles": { "dotfiles": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1782616428, "lastModified": 1782787572,
"narHash": "sha256-acd7rCZC9pS7McFEUyY+BrqVBiEEEipP0OVT5Rr/ZqI=", "narHash": "sha256-xWbUZXNE2xg1Svi1AVKVn75tLU29d+1GUZF8hX0N1hg=",
"ref": "main", "ref": "main",
"rev": "1a78b05e2a63b485aa04d609625fe4807ca562c8", "rev": "95ecb708933f49e539c43a71fcfcc5959f5739f2",
"revCount": 102, "revCount": 104,
"type": "git", "type": "git",
"url": "https://git.cpatino.com/c4patino/dotfiles" "url": "https://git.cpatino.com/c4patino/dotfiles"
}, },

@ -1 +1 @@
Subproject commit 1a78b05e2a63b485aa04d609625fe4807ca562c8 Subproject commit 95ecb708933f49e539c43a71fcfcc5959f5739f2

View file

@ -1,5 +1,6 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
pkgs, pkgs,
@ -20,12 +21,10 @@ in {
asciinema asciinema
]; ];
file = let file = {
crypt = "${config.snowfallorg.user.home.directory}/dotfiles/secrets/crypt"; ".config/asciinema/config.toml" = {
in { source = inputs.dotfiles + "/.config/asciinema/config.toml";
".config/asciinema/config.toml".source = };
"${crypt}/asciinema/config.toml"
|> config.lib.file.mkOutOfStoreSymlink;
}; };
}; };
}; };

View file

@ -1,5 +1,6 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
pkgs, pkgs,
@ -20,12 +21,10 @@ in {
rustypaste-cli rustypaste-cli
]; ];
file = let file = {
crypt = "${config.snowfallorg.user.home.directory}/dotfiles/secrets/crypt"; ".config/rustypaste/config.toml" = {
in { source = inputs.dotfiles + "/.config/rustypaste/config.toml";
".config/rustypaste/config.toml".source = };
"${crypt}/rustypaste/client.toml"
|> config.lib.file.mkOutOfStoreSymlink;
}; };
}; };

View file

@ -3,11 +3,10 @@
inputs, inputs,
lib, lib,
namespace, namespace,
pkgs,
... ...
}: let }: let
inherit (lib) mkIf mkForce; inherit (lib) mkIf mkForce;
inherit (lib.${namespace}) getAttrByNamespace getIn hostHasService readJsonOrEmpty resolveDatabaseHost resolveDatabaseIP resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace hostHasService resolveDatabaseHost resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -34,14 +33,7 @@ in {
SIGN_UP_DISABLED = true; SIGN_UP_DISABLED = true;
}; };
environmentFile = let environmentFile = config.sops.secrets."environment-file/asciinema".path;
secrets = readJsonOrEmpty "${inputs.self}/secrets/crypt/secrets.json";
ip = resolveDatabaseIP networkCfg.devices pgCfg.databases "asciinema";
password = getIn "postgresql.asciinema.password" secrets;
in
pkgs.writeText "asciinema.env" ''
DATABASE_URL=ecto://asciinema:${password}@${ip}:5600/asciinema
'';
}; };
networking.firewall.allowedTCPPorts = [port]; networking.firewall.allowedTCPPorts = [port];
@ -54,6 +46,8 @@ in {
}; };
}; };
sops.secrets."environment-file/asciinema" = {};
${namespace}.services.storage.impermanence.folders = [ ${namespace}.services.storage.impermanence.folders = [
{ {
directory = "/var/lib/asciinema"; directory = "/var/lib/asciinema";

View file

@ -1,13 +1,12 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
pkgs, pkgs,
... ...
}: let }: let
inherit (lib) mkIf mkForce; inherit (lib) mkIf mkForce;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP readJsonOrEmpty getIn hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -27,6 +26,10 @@ in {
database.type = "postgres"; database.type = "postgres";
secrets.database = {
PASSWD = config.sops.secrets."forgejo/db".path;
};
settings = { settings = {
actions.ENABLED = true; actions.ENABLED = true;
@ -51,10 +54,6 @@ in {
HOST = mkForce "${resolveDatabaseIP networkCfg.devices pgCfg.databases "forgejo"}:5600"; HOST = mkForce "${resolveDatabaseIP networkCfg.devices pgCfg.databases "forgejo"}:5600";
NAME = "forgejo"; NAME = "forgejo";
USER = "forgejo"; USER = "forgejo";
PASSWD =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty
|> getIn "postgresql.forgejo.password";
}; };
"git.timeout" = { "git.timeout" = {
@ -104,6 +103,8 @@ in {
}; };
}; };
sops.secrets."forgejo/db" = {};
${namespace}.services.storage.impermanence.folders = ["/var/lib/forgejo"]; ${namespace}.services.storage.impermanence.folders = ["/var/lib/forgejo"];
}; };
} }

View file

@ -23,6 +23,8 @@ in {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
environmentFile = config.sops.secrets."environment-file/glance".path;
settings = { settings = {
server = { server = {
host = "0.0.0.0"; host = "0.0.0.0";
@ -42,5 +44,7 @@ in {
source = inputs.dotfiles + "/.assets/icons/favicon.svg"; source = inputs.dotfiles + "/.assets/icons/favicon.svg";
mode = "0755"; mode = "0755";
}; };
sops.secrets."environment-file/glance" = {};
}; };
} }

View file

@ -1,12 +1,11 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
... ...
}: let }: let
inherit (lib) mkIf; inherit (lib) mkIf;
inherit (lib.${namespace}) getAttrByNamespace getIn readJsonOrEmpty hostHasService; inherit (lib.${namespace}) getAttrByNamespace hostHasService;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -331,37 +330,33 @@ in {
{ {
type = "group"; type = "group";
collapse-after = 5; collapse-after = 5;
widgets = let widgets = [
secrets =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty;
in [
{ {
type = "repository"; type = "repository";
title = "yumeami"; title = "yumeami";
repository = "c4patino/yumeami"; repository = "c4patino/yumeami";
token = getIn "github.glance" secrets; token = "\${GITHUB_TOKEN}";
commits-limit = 8; commits-limit = 8;
} }
{ {
type = "repository"; type = "repository";
title = "yumevim-nix"; title = "yumevim-nix";
repository = "c4patino/yumevim-nix"; repository = "c4patino/yumevim-nix";
token = getIn "github.glance" secrets; token = "\${GITHUB_TOKEN}";
commits-limit = 8; commits-limit = 8;
} }
{ {
type = "repository"; type = "repository";
title = "dotfiles"; title = "dotfiles";
repository = "c4patino/dotfiles"; repository = "c4patino/dotfiles";
token = getIn "github.glance" secrets; token = "\${GITHUB_TOKEN}";
commits-limit = 8; commits-limit = 8;
} }
{ {
type = "repository"; type = "repository";
title = "nixpkgs"; title = "nixpkgs";
repository = "nixos/nixpkgs"; repository = "nixos/nixpkgs";
token = getIn "github.glance" secrets; token = "\${GITHUB_TOKEN}";
} }
]; ];
} }

View file

@ -1,13 +1,11 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
pkgs,
... ...
}: let }: let
inherit (lib) mkIf; inherit (lib) mkIf;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP readJsonOrEmpty getIn hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
pgCfg = getAttrByNamespace config "${namespace}.services.storage.postgresql"; pgCfg = getAttrByNamespace config "${namespace}.services.storage.postgresql";
@ -39,10 +37,7 @@ in {
in "${ip}:5600"; in "${ip}:5600";
name = "grafana"; name = "grafana";
user = "grafana"; user = "grafana";
password = let password = "$__file{${config.sops.secrets."db/grafana".path}}";
secrets = readJsonOrEmpty "${inputs.self}/secrets/crypt/secrets.json";
grafanaPassword = pkgs.writeText "grafana-password.txt" (getIn "postgresql.grafana.password" secrets);
in "$__file{${grafanaPassword}}";
}; };
panels.enable_alpha = true; panels.enable_alpha = true;
@ -66,5 +61,14 @@ in {
RestartSec = lib.mkForce "1s"; RestartSec = lib.mkForce "1s";
}; };
}; };
sops.secrets = let
inherit (config.users.users) grafana;
in {
"db/grafana" = {
owner = grafana.name;
group = grafana.group;
};
};
}; };
} }

View file

@ -1,16 +1,14 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
... ...
}: let }: let
inherit (lib) mkIf mkForce; inherit (lib) mkIf;
inherit (lib.${namespace}) getAttrByNamespace readJsonOrEmpty getIn resolveDatabaseIP hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
pgCfg = getAttrByNamespace config "${namespace}.services.storage.postgresql";
isEnabled = hostHasService networkCfg.network-services hostName "ntfy"; isEnabled = hostHasService networkCfg.network-services hostName "ntfy";
port = resolveServicePort networkCfg.network-services "ntfy" 5201; port = resolveServicePort networkCfg.network-services "ntfy" 5201;
@ -18,6 +16,9 @@ in {
config = mkIf isEnabled { config = mkIf isEnabled {
services.ntfy-sh = { services.ntfy-sh = {
enable = true; enable = true;
environmentFile = config.sops.secrets."environment-file/ntfy".path;
settings = lib.mkForce { settings = lib.mkForce {
base-url = "http://ntfy.yumeami.sh"; base-url = "http://ntfy.yumeami.sh";
behind-proxy = true; behind-proxy = true;
@ -26,11 +27,6 @@ in {
upstream-base-url = "https://ntfy.sh"; upstream-base-url = "https://ntfy.sh";
attachment-cache-dir = "/var/lib/ntfy-sh/attachments"; attachment-cache-dir = "/var/lib/ntfy-sh/attachments";
database-url = let
pgPassword = getIn "postgresql.ntfy.password" (readJsonOrEmpty "${inputs.self}/secrets/crypt/secrets.json");
pgHost = resolveDatabaseIP networkCfg.devices pgCfg.databases "ntfy";
pgPort = 5600;
in "postgres://ntfy:${pgPassword}@${pgHost}:${toString pgPort}/ntfy?sslmode=disable";
auth-default-access = "deny-all"; auth-default-access = "deny-all";
enable-login = true; enable-login = true;
@ -53,9 +49,7 @@ in {
}; };
}; };
systemd.services.ntfy-sh.serviceConfig = { sops.secrets."environment-file/ntfy" = {};
DynamicUser = mkForce false;
};
networking.firewall.allowedTCPPorts = [port]; networking.firewall.allowedTCPPorts = [port];
}; };

View file

@ -6,8 +6,8 @@
pkgs, pkgs,
... ...
}: let }: let
inherit (lib) mkIf; inherit (lib) mkIf mkMerge;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost hostHasService resolveServicePort resolveDatabaseIP readJsonOrEmpty getIn; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost hostHasService resolveServicePort resolveDatabaseIP;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -187,23 +187,19 @@ in {
databasePort = 5600; databasePort = 5600;
databaseName = "qui"; databaseName = "qui";
databaseUser = "qui"; databaseUser = "qui";
databasePassword =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty
|> getIn "postgresql.qui.password";
}; };
}; };
systemd.services.qui = mkIf (dbHost == hostName) { systemd.services.qui = mkMerge [
after = ["postgresql.service"]; {
requires = ["postgresql.service"]; serviceConfig.EnvironmentFile = config.sops.secrets."environment-file/qui".path;
serviceConfig.RestartSec = "1s"; }
}; (mkIf (dbHost == hostName) {
after = ["postgresql.service"];
sops.secrets = { requires = ["postgresql.service"];
"openvpn" = {}; serviceConfig.RestartSec = "1s";
"qui" = {}; })
}; ];
users = { users = {
users = { users = {
@ -228,6 +224,12 @@ in {
}; };
}; };
sops.secrets = {
"environment-file/qui" = {};
"openvpn" = {};
"qui" = {};
};
${namespace}.services.storage.impermanence.folders = let ${namespace}.services.storage.impermanence.folders = let
qbittorrentUser = config.users.users.qbittorrent; qbittorrentUser = config.users.users.qbittorrent;
quiUser = config.users.users.qui; quiUser = config.users.users.qui;

View file

@ -51,10 +51,7 @@ in {
}; };
environment.etc."rustypaste/rustypaste.toml" = { environment.etc."rustypaste/rustypaste.toml" = {
source = let source = inputs.dotfiles + "/.config/rustypaste/server.toml";
crypt = "${inputs.self}/secrets/crypt/";
in "${crypt}/rustypaste/server.toml";
mode = "0755"; mode = "0755";
}; };

View file

@ -1,13 +1,12 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
pkgs, pkgs,
... ...
}: let }: let
inherit (lib) mkForce mkIf mkMerge; inherit (lib) mkForce mkIf mkMerge;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP readJsonOrEmpty getIn hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -31,10 +30,6 @@ in {
postgresPort = 5600; postgresPort = 5600;
postgresDatabase = "autobrr"; postgresDatabase = "autobrr";
postgresUser = "autobrr"; postgresUser = "autobrr";
postgresPass =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty
|> getIn "postgresql.autobrr.password";
postgresSSLMode = "disable"; postgresSSLMode = "disable";
}; };
}; };
@ -62,6 +57,7 @@ in {
User = autobrrUser.name; User = autobrrUser.name;
Group = autobrrUser.group; Group = autobrrUser.group;
UMask = mkForce "0002"; UMask = mkForce "0002";
EnvironmentFile = config.sops.secrets."environment-file/autobrr".path;
}; };
} }
(mkIf (dbHost == hostName) { (mkIf (dbHost == hostName) {
@ -73,10 +69,8 @@ in {
}; };
sops.secrets = { sops.secrets = {
"autobrr" = { "autobrr" = {};
owner = config.users.users.autobrr.name; "environment-file/autobrr" = {};
group = config.users.users.autobrr.group;
};
}; };
users = { users = {

View file

@ -1,12 +1,11 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
... ...
}: let }: let
inherit (lib) mkForce mkIf mkMerge; inherit (lib) mkForce mkIf mkMerge;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP readJsonOrEmpty getIn hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -25,10 +24,6 @@ in {
systemd.services.bazarr = let systemd.services.bazarr = let
bazarrUser = config.users.users.bazarr; bazarrUser = config.users.users.bazarr;
dbIp = resolveDatabaseIP networkCfg.devices pgCfg.databases "bazarr"; dbIp = resolveDatabaseIP networkCfg.devices pgCfg.databases "bazarr";
password =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty
|> getIn "postgresql.bazarr.password";
in in
mkMerge [ mkMerge [
{ {
@ -38,7 +33,6 @@ in {
POSTGRES_PORT = "5600"; POSTGRES_PORT = "5600";
POSTGRES_DATABASE = "bazarr"; POSTGRES_DATABASE = "bazarr";
POSTGRES_USERNAME = "bazarr"; POSTGRES_USERNAME = "bazarr";
POSTGRES_PASSWORD = password;
}; };
serviceConfig = { serviceConfig = {
@ -46,6 +40,7 @@ in {
User = bazarrUser.name; User = bazarrUser.name;
Group = bazarrUser.group; Group = bazarrUser.group;
UMask = mkForce "0002"; UMask = mkForce "0002";
EnvironmentFile = config.sops.secrets."environment-file/bazarr".path;
}; };
} }
(mkIf (dbHost == hostName) { (mkIf (dbHost == hostName) {
@ -65,6 +60,8 @@ in {
groups.bazarr = {}; groups.bazarr = {};
}; };
sops.secrets."environment-file/bazarr" = {};
${namespace}.services.storage.impermanence.folders = let ${namespace}.services.storage.impermanence.folders = let
bazarrUser = config.users.users.bazarr; bazarrUser = config.users.users.bazarr;
in [ in [

View file

@ -1,12 +1,11 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
... ...
}: let }: let
inherit (lib) mkForce mkIf mkMerge; inherit (lib) mkForce mkIf mkMerge;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP readJsonOrEmpty getIn hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -19,6 +18,11 @@ in {
config = mkIf isEnabled { config = mkIf isEnabled {
services.lidarr = { services.lidarr = {
enable = true; enable = true;
environmentFiles = [
config.sops.secrets."environment-file/lidarr".path
];
settings = { settings = {
server.port = port; server.port = port;
postgres = { postgres = {
@ -27,10 +31,6 @@ in {
user = "lidarr"; user = "lidarr";
maindb = "lidarr"; maindb = "lidarr";
logdb = "lidarr-log"; logdb = "lidarr-log";
password =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty
|> getIn "postgresql.lidarr.password";
}; };
}; };
}; };
@ -64,6 +64,8 @@ in {
groups.lidarr = {}; groups.lidarr = {};
}; };
sops.secrets."environment-file/lidarr" = {};
${namespace}.services.storage.impermanence.folders = let ${namespace}.services.storage.impermanence.folders = let
lidarrUser = config.users.users.lidarr; lidarrUser = config.users.users.lidarr;
in [ in [

View file

@ -1,12 +1,11 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
... ...
}: let }: let
inherit (lib) mkForce mkIf mkMerge; inherit (lib) mkForce mkIf mkMerge;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP readJsonOrEmpty getIn hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -19,6 +18,11 @@ in {
config = mkIf isEnabled { config = mkIf isEnabled {
services.prowlarr = { services.prowlarr = {
enable = true; enable = true;
environmentFiles = [
config.sops.secrets."environment-file/prowlarr".path
];
settings = { settings = {
server.port = port; server.port = port;
postgres = { postgres = {
@ -27,10 +31,6 @@ in {
user = "prowlarr"; user = "prowlarr";
maindb = "prowlarr"; maindb = "prowlarr";
logdb = "prowlarr-log"; logdb = "prowlarr-log";
password =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty
|> getIn "postgresql.prowlarr.password";
}; };
}; };
}; };
@ -64,6 +64,8 @@ in {
groups.prowlarr = {}; groups.prowlarr = {};
}; };
sops.secrets."environment-file/prowlarr" = {};
${namespace}.services.storage.impermanence.folders = let ${namespace}.services.storage.impermanence.folders = let
prowlarrUser = config.users.users.prowlarr; prowlarrUser = config.users.users.prowlarr;
in [ in [

View file

@ -1,12 +1,11 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
... ...
}: let }: let
inherit (lib) mkForce mkIf mkMerge; inherit (lib) mkForce mkIf mkMerge;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP readJsonOrEmpty getIn hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -19,6 +18,11 @@ in {
config = mkIf isEnabled { config = mkIf isEnabled {
services.radarr = { services.radarr = {
enable = true; enable = true;
environmentFiles = [
config.sops.secrets."environment-file/radarr".path
];
settings = { settings = {
server.port = port; server.port = port;
postgres = { postgres = {
@ -27,10 +31,6 @@ in {
user = "radarr"; user = "radarr";
maindb = "radarr"; maindb = "radarr";
logdb = "radarr-log"; logdb = "radarr-log";
password =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty
|> getIn "postgresql.radarr.password";
}; };
}; };
}; };
@ -64,6 +64,8 @@ in {
groups.radarr = {}; groups.radarr = {};
}; };
sops.secrets."environment-file/radarr" = {};
${namespace}.services.storage.impermanence.folders = let ${namespace}.services.storage.impermanence.folders = let
radarrUser = config.users.users.radarr; radarrUser = config.users.users.radarr;
in [ in [

View file

@ -1,12 +1,11 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
... ...
}: let }: let
inherit (lib) mkForce mkIf mkMerge; inherit (lib) mkForce mkIf mkMerge;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP readJsonOrEmpty getIn hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -26,10 +25,6 @@ in {
systemd.services.seerr = let systemd.services.seerr = let
seerrUser = config.users.users.seerr; seerrUser = config.users.users.seerr;
dbIp = resolveDatabaseIP networkCfg.devices pgCfg.databases "seerr"; dbIp = resolveDatabaseIP networkCfg.devices pgCfg.databases "seerr";
password =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty
|> getIn "postgresql.seerr.password";
in in
mkMerge [ mkMerge [
{ {
@ -38,7 +33,6 @@ in {
DB_HOST = dbIp; DB_HOST = dbIp;
DB_PORT = "5600"; DB_PORT = "5600";
DB_USER = "seerr"; DB_USER = "seerr";
DB_PASS = password;
DB_NAME = "seerr"; DB_NAME = "seerr";
}; };
@ -46,6 +40,7 @@ in {
DynamicUser = mkForce false; DynamicUser = mkForce false;
User = seerrUser.name; User = seerrUser.name;
Group = seerrUser.group; Group = seerrUser.group;
EnvironmentFile = config.sops.secrets."environment-file/seerr".path;
}; };
} }
(mkIf (dbHost == hostName) { (mkIf (dbHost == hostName) {
@ -64,6 +59,8 @@ in {
groups.seerr = {}; groups.seerr = {};
}; };
sops.secrets."environment-file/seerr" = {};
${namespace}.services.storage.impermanence.folders = let ${namespace}.services.storage.impermanence.folders = let
seerrUser = config.users.users.seerr; seerrUser = config.users.users.seerr;
in [ in [

View file

@ -1,12 +1,11 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
... ...
}: let }: let
inherit (lib) mkForce mkIf mkMerge; inherit (lib) mkForce mkIf mkMerge;
inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP readJsonOrEmpty getIn hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -19,6 +18,11 @@ in {
config = mkIf isEnabled { config = mkIf isEnabled {
services.sonarr = { services.sonarr = {
enable = true; enable = true;
environmentFiles = [
config.sops.secrets."environment-file/sonarr".path
];
settings = { settings = {
server.port = port; server.port = port;
postgres = { postgres = {
@ -27,10 +31,6 @@ in {
user = "sonarr"; user = "sonarr";
maindb = "sonarr"; maindb = "sonarr";
logdb = "sonarr-log"; logdb = "sonarr-log";
password =
"${inputs.self}/secrets/crypt/secrets.json"
|> readJsonOrEmpty
|> getIn "postgresql.sonarr.password";
}; };
}; };
}; };
@ -64,6 +64,8 @@ in {
groups.sonarr = {}; groups.sonarr = {};
}; };
sops.secrets."environment-file/sonarr" = {};
${namespace}.services.storage.impermanence.folders = let ${namespace}.services.storage.impermanence.folders = let
sonarrUser = config.users.users.sonarr; sonarrUser = config.users.users.sonarr;
in [ in [

View file

@ -5,22 +5,23 @@
pkgs, pkgs,
... ...
}: let }: let
inherit (lib) filter mkIf optional; inherit (lib) filter mkIf optional mapAttrs' nameValuePair toUpper;
inherit (lib.${namespace}) getAttrByNamespace hostHasService mkOutOfStoreSymlink; inherit (lib.${namespace}) getAttrByNamespace hostHasService;
inherit (config.users.users) unpackerr;
inherit (config.networking) hostName; inherit (config.networking) hostName;
networkCfg = getAttrByNamespace config "${namespace}.services.networking";
starrServices = ["radarr" "sonarr" "lidarr"]; starrServices = ["radarr" "sonarr" "lidarr"];
networkCfg = getAttrByNamespace config "${namespace}.services.networking";
enabledStarrServices = filter (service: hostHasService networkCfg.network-services hostName service) starrServices; enabledStarrServices = filter (service: hostHasService networkCfg.network-services hostName service) starrServices;
isEnabled = enabledStarrServices != []; isEnabled = enabledStarrServices != [];
in { in {
config = mkIf isEnabled { config = mkIf isEnabled {
systemd.services.unpackerr = let systemd.services.unpackerr = let
unpackerrUser = config.users.users.unpackerr;
systemdDependencies = systemdDependencies =
enabledStarrServices enabledStarrServices
|> map (service: "${service}.service"); |> map (service: "${service}.service");
mkUnEnv = mapAttrs' (name: value: nameValuePair "UN_${toUpper name}" value);
in { in {
description = "Extract downloads for Starr apps"; description = "Extract downloads for Starr apps";
@ -28,21 +29,36 @@ in {
wants = ["network-online.target"] ++ systemdDependencies; wants = ["network-online.target"] ++ systemdDependencies;
after = ["network-online.target"] ++ systemdDependencies; after = ["network-online.target"] ++ systemdDependencies;
serviceConfig = let environment = mkUnEnv {
cfg = debug = "false";
"${config.users.users.c4patino.home}/dotfiles/secrets/crypt/unpackerr.toml" quiet = "false";
|> mkOutOfStoreSymlink pkgs;
in { interval = "2m";
start_delay = "1m";
retry_delay = "5m";
max_retries = "10";
parallel = "1";
file_mode = "0644";
dir_mode = "0755";
log_files = "10";
log_file_mb = "10";
log_queues = "1m";
error_stderr = "false";
activity = "false";
};
serviceConfig = {
Type = "simple"; Type = "simple";
User = unpackerrUser.name; User = unpackerr.name;
Group = unpackerrUser.group; Group = unpackerr.group;
UMask = "0002"; UMask = "0002";
StateDirectory = "unpackerr"; StateDirectory = "unpackerr";
EnvironmentFile = config.sops.secrets."environment-file/unpackerr".path;
LoadCredential = ["unpackerr.toml:${cfg}"]; ExecStart = "${pkgs.unpackerr}/bin/unpackerr";
ExecStart = "${pkgs.unpackerr}/bin/unpackerr --config %d/unpackerr.toml";
Restart = "always"; Restart = "always";
RestartSec = 30; RestartSec = 30;
}; };
@ -58,17 +74,17 @@ in {
groups.unpackerr = {}; groups.unpackerr = {};
}; };
${namespace}.services.storage.impermanence.folders = let ${namespace}.services.storage.impermanence.folders = [
unpackerrUser = config.users.users.unpackerr;
in [
{ {
directory = "/var/lib/unpackerr"; directory = "/var/lib/unpackerr";
user = unpackerrUser.name; user = unpackerr.name;
group = unpackerrUser.group; group = unpackerr.group;
mode = "700"; mode = "700";
} }
]; ];
sops.secrets."environment-file/unpackerr" = {};
systemd.services.radarr.upholds = optional (builtins.elem "radarr" enabledStarrServices) "unpackerr.service"; systemd.services.radarr.upholds = optional (builtins.elem "radarr" enabledStarrServices) "unpackerr.service";
systemd.services.sonarr.upholds = optional (builtins.elem "sonarr" enabledStarrServices) "unpackerr.service"; systemd.services.sonarr.upholds = optional (builtins.elem "sonarr" enabledStarrServices) "unpackerr.service";
systemd.services.lidarr.upholds = optional (builtins.elem "lidarr" enabledStarrServices) "unpackerr.service"; systemd.services.lidarr.upholds = optional (builtins.elem "lidarr" enabledStarrServices) "unpackerr.service";

View file

@ -110,11 +110,17 @@ in {
]; ];
}; };
environment.etc."munge/munge.key" = { sops.secrets = let
source = "${inputs.self}/secrets/crypt/munge.key"; inherit (config.users.users) munge;
user = "munge"; in {
group = "munge"; "munge-key" = {
mode = "0400"; sopsFile = "${inputs.self}/secrets/sops/munge.key";
format = "binary";
path = "/etc/munge/munge.key";
owner = munge.name;
group = munge.group;
mode = "0400";
};
}; };
${namespace}.services.storage.impermanence.folders = [ ${namespace}.services.storage.impermanence.folders = [

View file

@ -1,13 +1,12 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
pkgs, pkgs,
... ...
}: let }: let
inherit (lib) mkIf; inherit (lib) mkIf mkMerge;
inherit (lib.${namespace}) getAttrByNamespace readJsonOrEmpty getIn resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort; inherit (lib.${namespace}) getAttrByNamespace resolveDatabaseHost resolveDatabaseIP hostHasService resolveServicePort;
inherit (config.networking) hostName; inherit (config.networking) hostName;
pgCfg = getAttrByNamespace config "${namespace}.services.storage.postgresql"; pgCfg = getAttrByNamespace config "${namespace}.services.storage.postgresql";
@ -16,6 +15,9 @@
isEnabled = hostHasService networkCfg.network-services hostName "vault"; isEnabled = hostHasService networkCfg.network-services hostName "vault";
port = resolveServicePort networkCfg.network-services "vault" 5400; port = resolveServicePort networkCfg.network-services "vault" 5400;
dbHost = resolveDatabaseHost pgCfg.databases "vaultwarden";
dbIp = resolveDatabaseIP networkCfg.devices pgCfg.databases "vaultwarden";
in { in {
config = mkIf isEnabled { config = mkIf isEnabled {
services.vaultwarden = { services.vaultwarden = {
@ -31,11 +33,6 @@ in {
SIGNUPS_ALLOWED = false; SIGNUPS_ALLOWED = false;
SIGNUPS_VERIFY = false; SIGNUPS_VERIFY = false;
DATABASE_URL = let
secrets = readJsonOrEmpty "${inputs.self}/secrets/crypt/secrets.json";
ip = resolveDatabaseIP networkCfg.devices pgCfg.databases "vaultwarden";
in ''postgresql://vaultwarden:${getIn "postgresql.vaultwarden.password" secrets}@${ip}:5600/vaultwarden'';
LOG_LEVEL = "Info"; LOG_LEVEL = "Info";
ROCKET_ADDRESS = "0.0.0.0"; ROCKET_ADDRESS = "0.0.0.0";
@ -43,24 +40,29 @@ in {
ROCKET_LOG = "critical"; ROCKET_LOG = "critical";
EXPERIMENTAL_CLIENT_FEATURE_FLAGS = "autofill-v2,extension-refresh,ssh-key-vault-item,ssh-agent"; EXPERIMENTAL_CLIENT_FEATURE_FLAGS = "autofill-v2,extension-refresh,ssh-key-vault-item,ssh-agent";
DATABASE_URL = "postgresql://";
}; };
}; };
networking.firewall.allowedTCPPorts = [port]; systemd.services.vaultwarden = mkMerge [
{
systemd.services.vaultwarden = let environment = {
dbHost = resolveDatabaseHost pgCfg.databases "vaultwarden"; PGHOST = dbIp;
in PGPORT = "5600";
mkIf (dbHost == config.networking.hostName) { PGDATABASE = "vaultwarden";
PGUSER = "vaultwarden";
};
}
(mkIf (dbHost == config.networking.hostName) {
after = ["postgresql.service" "pgbouncer.service"]; after = ["postgresql.service" "pgbouncer.service"];
requires = ["postgresql.service" "pgbouncer.service"]; requires = ["postgresql.service" "pgbouncer.service"];
serviceConfig = { serviceConfig.RestartSec = "1s";
RestartSec = "1s"; })
}; ];
};
sops.secrets = { sops.secrets."vaultwarden" = {};
"vaultwarden" = {};
}; networking.firewall.allowedTCPPorts = [port];
}; };
} }

View file

@ -100,14 +100,7 @@ in {
groups.github-runner = {}; groups.github-runner = {};
}; };
sops.secrets = let sops.secrets."github/runner" = {};
inherit (config.users.users) github-runner;
in {
"github/runner" = {
owner = github-runner.name;
group = github-runner.group;
};
};
${namespace}.services.storage.impermanence.folders = ["/var/lib/github-runner"]; ${namespace}.services.storage.impermanence.folders = ["/var/lib/github-runner"];
}; };

View file

@ -1,12 +1,11 @@
{ {
config, config,
inputs,
lib, lib,
namespace, namespace,
... ...
}: let }: let
inherit (lib) mapAttrs'; inherit (lib) mapAttrs';
inherit (lib.${namespace}) getAttrByNamespace resolveHostIP readJsonOrEmpty getIn; inherit (lib.${namespace}) getAttrByNamespace resolveHostIP;
cfg = getAttrByNamespace config "${namespace}.services.storage.samba"; cfg = getAttrByNamespace config "${namespace}.services.storage.samba";
networkCfg = getAttrByNamespace config "${namespace}.services.networking"; networkCfg = getAttrByNamespace config "${namespace}.services.networking";
@ -26,7 +25,7 @@ in {
device = "//${hostIP}/${mountCfg.folder}"; device = "//${hostIP}/${mountCfg.folder}";
fsType = "cifs"; fsType = "cifs";
options = [ options = [
"${automount_opts},credentials=/etc/samba/.credentials,uid=1000,gid=100" "${automount_opts},credentials=${config.sops.secrets."samba".path},uid=1000,gid=100"
]; ];
}; };
}; };

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

34
secrets/sops/munge.key Normal file
View file

@ -0,0 +1,34 @@
{
"data": "ENC[AES256_GCM,data:VpttoYzmvsM3ffMoOmSZvmdrvRR3JvWDxnLmYFY76Tv8ffeh1yt6qrNmO8aHO9J0CfAxYsG/tGnpDJNIFSUtG/1rVcBZQ/mH8PVY+qp/2GTf9h42kZdie7Y+F69+1emj8NnYJv1/zruYo14u3N/B0axKMV1Hd/wFUcTrohAAcbmL,iv:mFOrPNGCM7THXA1UrpyIdneRmwMiXze/YNMu/BUH6zc=,tag:WWxPyE0hwB4/nF8LUImrMA==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRQ1A0TjdvQnlaUGw4Q2I2\nalpnbGJNcnIwWFZIb3U4bHVQVCtqL3FjazJJCm1GOUlYeXAzOGNRSllGNmhVd1RP\ncmtobHpMR1F1LzNhb2htRUp6V2pocTAKLS0tIGhGMUpWSGZ6YlRqNk8vbElwSDZh\nUFQxbFdaTTM5Mm1ZMjFaR0NnR2xRS0EKKd3RFNdEF2cRXWLtE3EdG2WXVGBf2RCx\n9x0kmTDWqPs9Xj0Cfb3St2Fdpu9Q7FTvogCxZoLZc8OPgSlqssUueA==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1q4c2fy5hrjg5pww86k3wsamknc67vxtfc7k5he5exndwag5fjehqv4kymv"
},
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0REgrWVpJcVgvNEJMSGp1\nQlJMSjZQYmZLeERhRzQvSzdEUWRWSmQ5VmlvCngyV09veCsza24wVVk0UVRRVk9N\naC9zN0MrbmhYbW5VckxwaS9lWEQ5ZFEKLS0tIG13SXB6NnZxOXZ6dG15aWpwVWpK\nNFlNTDBiaVVLZTFYb05UYVRJdUc1N0kKB9+mOhrM56jJ+j+M+6vaWBmSsqMv1Yci\nRkA1K1l5nZNjOX60hmjHAtd83hTofyuQREgQkN6fCvCeJs0gvADqiA==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1redpndk4tj0jy6d8cl98rkt5euxzpfzc8dvlhfan5a349tl7tg5s5j6zav"
},
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGN1NRaTVOQlN0eHZzUGQ4\nbStQTXZxcnRKSEZXTUhuOXdzQmdQWTUvaFFzCjJMZ08xMWlvemdNckw0S3R3ZWtl\nTHNwbndOQytxSWk2UG9xRDF4WmFOejgKLS0tIFVqbC9tQVFRYmd6bzEraC9SMmhR\nMWN0WDBUK3l5UHdGZm5zbkVrYnI4OGMKvdyANYaOz1NhuBwP2PV/bBUJtUe2eaD8\nSf5GcVdJO34G+nSO9T31bTwnCYB0IFCEJSV1BIPalPhyTFOHioTHAg==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1u0jz3r6tz3a7rz64j02xpwuykslq9zruxasutvmum07ya7k3xduseme93n"
},
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWYnRyN3B1c2tYeU1HUnlr\nV3U1bE95UnNxc0xIam5hdmxiQkJIeEhmZ1VzClBMcmRPaENscmlVaE4xZjVZVXNm\neXBEdU41dnFrb0M0alR1RDhxaE5CM1kKLS0tIDZaR0xWOGFDaUppeWQ1OGIrVFlk\na2VNMXR2N0NDNUs2QUNZRVprQzRSa2sKgy02COgHNe59yTH2L/1Wse5TOAI+xXuO\n+DlMvxAEW5olNMNDhZEfSBJfjtFkMhIT/eKiGIuz1cdB7j+6RJuiIw==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1r9285z8xmtj7g6alau7xp946p4fguvtd62dz05nvfgxyujhmfs7svp0uyw"
},
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEWVMveFRJSUJRQ1NVN0VR\nMnZXRW01SWdaMFptejBhS3NVRExKbzJWVlZRClJvRXp5bVpaRmIxdlh6dVVnVXlG\nOFp5RmZDU2h0bEQ1Unc0bmJmM1hsOEkKLS0tIFlhUVVlUWp5d2FkWUpUWWNJMVpR\neVBEcUNibVB5RldmeVdwdnVxRFd3WTgKePuyoy+7F85dbq3mmPSrU1gavbGFk+BX\nt4tmq6nbfCbsvjvVahv5EpKHYof0ceEp44T3D+wGLySW+MGQ5OUUfQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age185cchggrqp8v9wryj7npdvv3h2z735zamtqgchu5tltmjmpsaehsteyrpk"
},
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBId3lzeDhzMGJCWFZzTXBS\nSDhZTlcyQmMvM3R3YXoyK1huTUdxZm1qcVJRCk56ejRqbFAzc3l5Z0YrS3hoT0ky\neUxsaEJkSFpITEt0NzJMano0NnlDdW8KLS0tIExaOHVReDBlblZEZi9KS1RoQ21J\nMC9oaDZ4MHptL2hhcWl4YjA1T21iVU0KKPuxtcZZQJQQb4O+CNFq/AGwm45zoH2d\njzuXgXQIGmPAvzCvMjCVYqnFktgNW0dpk5ZmVp3nDzxrEy66vPqW9g==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1uvdnhvu83fafnvvkfwx5e3jt7qfqppytqu0rnhwtc3ruyatrxsgqwzjvjz"
}
],
"lastmodified": "2026-06-30T03:02:07Z",
"mac": "ENC[AES256_GCM,data:2fnDqWgfY8/LYNI7vt3Y7Lqis/4DQOmGkbUhPvlxaZDnOaLOzkmy0yO3MFYTwqs1zL4aPSePtBtDlTra27B7hnvnWAY5b5NVnGh1MXJyBWXcPuYS6TYHX4F6ndQQ9Ye1WcweEneu/i31RBZDRoLEN/XWYh9Fh8BCkJ29TYX51N8=,iv:icaqhSAucN4CFyy7kV/pRaaW/L+NXU9pgJJDXIBXBsQ=,tag:+nOYO/HPMP4lxbaF31LY3w==,type:str]",
"version": "3.13.1"
}
}

View file

@ -9,7 +9,23 @@ cloudflare:
tunnel: tunnel:
certificate: ENC[AES256_GCM,data:z+TulSaF38bztUk9GNW8s56e64siBOKsaDGV5YFX64eXiJ8qfiAR2uhFSchi0TFjKDs3Lrx68Lk8LI8n3Ta1AuZowpskMoC/vqyzK8nFlYp0KTDkKI0W2leoyJqxvlgqL8y+dPBjibq6WZvZfLCPEvwx6XggqKNSqc3dw8lVy0TEHnXdzbBQ6WarYlEb93rNu9UY5u5eRET01efpVKFXgwaMuBshYRVhEUci0ejO4LWahYXOd0taBWR5zfe9AmWATILq6fvdFVu5eRY2fzcMORvDAACr79E8cMyJdIhLmbSndQvMttAS/xOvRiiLJSf+0Dj3bEPTexfI8oSL688B15zwU+EYu+tpTV4=,iv:Fsdtl3TyDda+ewQW0VnVhScRAqeuH+ypAl/USMSGrt8=,tag:O72uBGHqZRN+byhDPCepCQ==,type:str] certificate: ENC[AES256_GCM,data:z+TulSaF38bztUk9GNW8s56e64siBOKsaDGV5YFX64eXiJ8qfiAR2uhFSchi0TFjKDs3Lrx68Lk8LI8n3Ta1AuZowpskMoC/vqyzK8nFlYp0KTDkKI0W2leoyJqxvlgqL8y+dPBjibq6WZvZfLCPEvwx6XggqKNSqc3dw8lVy0TEHnXdzbBQ6WarYlEb93rNu9UY5u5eRET01efpVKFXgwaMuBshYRVhEUci0ejO4LWahYXOd0taBWR5zfe9AmWATILq6fvdFVu5eRY2fzcMORvDAACr79E8cMyJdIhLmbSndQvMttAS/xOvRiiLJSf+0Dj3bEPTexfI8oSL688B15zwU+EYu+tpTV4=,iv:Fsdtl3TyDda+ewQW0VnVhScRAqeuH+ypAl/USMSGrt8=,tag:O72uBGHqZRN+byhDPCepCQ==,type:str]
credentials: ENC[AES256_GCM,data:X/GQzuIwpIWeAab4twWJd+RjgAzm/S6d+u6Rfnvk/Xwg7+DwNzDpNjm7H8xYeD6sNABIBkq51JFbfldzSYwM3IuoqD/C470Yu5IZrL1Vbv8oR9XWNh5MqLpuVJ30nTbUzG062T7xAZ/uFhLZIovgcAvyBhltlh1Fgq0PaZh9jCAJxGZwTtv/m2qXwntQiE7as2pyk4eTBeVC0V+2ud3aALhLZd5BWvL01INMx0O33NIQPgKO9JKLi1InuuE1bolK8Q==,iv:3bPfPvM+EGnlsllsshHqTv2Aaa3s4vP5Zlx0oYCgm3c=,tag:qLH9tgK/eGoOPuGAaPTU8A==,type:str] credentials: ENC[AES256_GCM,data:X/GQzuIwpIWeAab4twWJd+RjgAzm/S6d+u6Rfnvk/Xwg7+DwNzDpNjm7H8xYeD6sNABIBkq51JFbfldzSYwM3IuoqD/C470Yu5IZrL1Vbv8oR9XWNh5MqLpuVJ30nTbUzG062T7xAZ/uFhLZIovgcAvyBhltlh1Fgq0PaZh9jCAJxGZwTtv/m2qXwntQiE7as2pyk4eTBeVC0V+2ud3aALhLZd5BWvL01INMx0O33NIQPgKO9JKLi1InuuE1bolK8Q==,iv:3bPfPvM+EGnlsllsshHqTv2Aaa3s4vP5Zlx0oYCgm3c=,tag:qLH9tgK/eGoOPuGAaPTU8A==,type:str]
db:
grafana: ENC[AES256_GCM,data:tLs0xZsjUtYaL7CioXzSxYYRh5rP4kjfGYIkUR6B3QNBE0JVTs3HTLd+xCM=,iv:mNqWaxc1KgVJ7FcKmEYFoce6RQRopK/Fy8svyTpPgJ4=,tag:GwIwPqF1Zyj6Agv+WZkYog==,type:str]
environment-file:
asciinema: ENC[AES256_GCM,data:fMFY/jyr/LISb9RTItVvXVx8/eeSylwVSPcz/k3eH8XPVGmF1SOiRQzAP15u5mdEDjwhlf8fVW1oFLej8toTFVH379CgkllfIFlAKyhRgc34kOImS6px/yoKk6uyT8fOqkQTMTHR,iv:wQY/WRlCl+2VqgBRbNA9dZFSwEwKgFYm3i3z/l1Ibdc=,tag:1wI5X9RoOnNmfnYyYIp/zA==,type:str]
autobrr: ENC[AES256_GCM,data:EXuYnWKHk0cwFWdMUaU/aapaPlY4o0/WjwlBenIM9aqOQ9jZ8bqQEcSeP8iHg9XjDJFNpxwSTsDCp3sB6jXrqAbR6Q==,iv:rhRvRE5+1VfcGcWjm4rGrs8fPzttp5WgHGpjnwcgSV4=,tag:RNK3Xp+NGeUSOgWC9uUsCA==,type:str]
bazarr: ENC[AES256_GCM,data:vDTjMrKEoGdnx1ABTAuA0k3kogR2tV4z9jvXVj52B2YVDRa4MPN/CWRXtAR1u0jysf7rE3NJMsBv3nQgOpI=,iv:ahQlJt+aok0vkgVD85qdpIEX0rxE9qcUOQWH7abqa0w=,tag:DzogkL9XBDJNBsiUgBwHIw==,type:str]
glance: ENC[AES256_GCM,data:/T/ItEwjzvLj1/p3ldFu+dorzOWUSDcxrljPiYJ1j/9PIJZMJkZ2BYIXIt8FwwnA1Omwl6QoJwN9H/GiT2we69ucVnic0i3SchDHuM3rXA9/xuvUB61rzXdx3fQ9T7LbJ6cA77oApCBCOA==,iv:LlsIVEhUot5YJpTzi6uvL/i2hrfAxnP3pZOOV9L8Xvs=,tag:/Fnh+WgtZXfSb9pWe1m1Mw==,type:str]
lidarr: ENC[AES256_GCM,data:F75i9YOqcFPVICggm+JHJB+jor2PNAAfyu/Zsj+zptqtJh0nJMMpfctuEYRyc1k0y/VGssPDu8FqEqH44kmgn4L1cmsLcAM=,iv:6JvmnIAG4GUebD7DoLrGt/G/tUrbAiZUDlGZUHxxmbY=,tag:U9wB+cKXM3kSnbADmfTmzw==,type:str]
ntfy: ENC[AES256_GCM,data:JB+h4r+QXerD8PbT35RwM556RY63mTNvBN31rIZA7gVkqfa33tLco1fNkFyysA+qpTKaraKpGgmlC6yBgWi5dc7My2CzEHKqqEAV6CJ24Tj00F1aXOunw8BsEBCYuVeHpbIsEtikbkTQ6d0r5bAXdlrL8zyD,iv:/zLYKwDcaNWR3lBqH+IPuwsPnq59SQHaMGSEY+ADJ/w=,tag:+WMzRX+YPaW8dMvJsAAcVQ==,type:str]
prowlarr: ENC[AES256_GCM,data:l1aDFg/alG7CDkkwufzMjQ2/6ZLnMvBO/GodS2txktvjRCeoc+qfL4Xf4T6JCltVHTHTl8P3Y0GlWoZ0VPbuP/L+UmV/jZKBvA==,iv:xnXc5TGd1zpDCvaN2IzdI2R/t8C0hTJaLVsP6u3fNsU=,tag:4ZkRyc1aCZoY59X9zIV5gw==,type:str]
qui: ENC[AES256_GCM,data:V1Dk5xU2sMNznU6RiKcTiDVPAhD6BKDS4sxTuFQpXEbE8n8FXmmE14buE+z9EazVZ8U+rkXx+f5IyX2y0cLWkL3zVg==,iv:cX1Nste48K01l+i+/UKNVKxhn/tRB6KhWaYZMRpYbrM=,tag:d9ARrZl43rzqz7NiFZ23Uw==,type:str]
radarr: ENC[AES256_GCM,data:+dkuKf0D+RmPmkecZ8x96eYhtcjcqC6TNOoHiNUAI2XSYn2Eqr8T0kmjl1vzp0lwq5x+4NldhVjR3ljgKbTUXdRlJj/nGJw=,iv:ycA4gCJaSTKpZlk35pBFlTWrpYU9DCH8MbL8eC5vVWU=,tag:7AAZblzOD5TFy5DmB7UDZg==,type:str]
seerr: ENC[AES256_GCM,data:XdYLSnp0feoPAFtxkfZukyGd7/pXagYBsCx5c8yDCnPMvW5LtqTLqZwJSCSqa2kzEQW3UA==,iv:Cb+PR08Fpj8hzjnz4nEijuQTX/WwUG4bBEqPpkWZkUo=,tag:4FrVJ4pXhQK2RTK9uLeLNg==,type:str]
sonarr: ENC[AES256_GCM,data:in++Xn5bHYEUHAL1vxekTIc/hcc/1fSfVfmA2d/m2rdpt2AP8kN6EgllcmiTdPRRMvQ5Pjqr7c/20J4UOa8uXh9rZt3HUZk=,iv:OITH/vGCxiG6wVth6fLWULf7jqo3KE+Ll76SzY5zVWk=,tag:oRHYYmbA0qL96qW7vEABRA==,type:str]
unpackerr: ENC[AES256_GCM,data:LJk5hDuiLmEOnExDCUNKEuHpFKaQIV4un0brZugmIjNPIYp5HNZYf2vcpwJSlR2lR1lc7BmJyKHizG81NUMkzFBe/cBEOtmHT1extN6r9dKUSHDJZUzi6H5ZFq0KdKGf2mo9fwpOl36OQZW1Y6aLDYzgQmcCBQYvX7FuTRKuUztRm+CzhCtcOCcTw/W31t0OMhuYe/zC7RIbvdC9j2oaY5ltUUGOp1QUtQ0+5n82OxD/2/KhwfsQXmaElfuxwaQqNfY+LZYepy8wbZyEDGKanpOAwJGJEIK57aEHna8pOCG/rh99VPAA3N5bMopsfrulBpRwoqp15XswXcilv1P3aycHIXtMX3kaeiz9IKVMCQAroCtXmu0qeMsxBVYKKVdP8juyap8h857iTPHt7swqY2aQy910xIB5jYpwpm3SWIXjZ5etE54SJy5/VKSoIbYl1uq6ofKoGn6HEPpGnhOhbvs9EpCWBIbS5rh/mMd8qkYJTQNhkrUpeTInPnSyUekevVUtKlZufmp61Kv9A4HVdGwhT+0qm1YTvE5LIjloHlLz,iv:Hyk27zl2caYwhJr8HgKhEN/mXGHDxEd+cRKZTvDGpWg=,tag:k41W9jUK6CmI3PyN6dmAug==,type:str]
forgejo: forgejo:
db: ENC[AES256_GCM,data:P19bNXiDzTaH6mq0X4jDFMkT/EVO2CMWmqpbnT8610djV10MFaYC1brazgE=,iv:vsJxd99Og1Fn4elzkPgAPitF3iDIdcO7MPpxR3Ifb8w=,tag:F1xO6AYCV2YKtDs9KqWS5w==,type:str]
token: ENC[AES256_GCM,data:+y5ZOxfLAV9cg2l6XAh6odPjLgiKiQ/VqnEkhSRhi5+hdgkUvhSQ/MVjWeCvJQ==,iv:SzTU8F3uo2HTI2viFrkdtDK80tvf8LsgN4iftGv7XME=,tag:z3dQH8lwAvzc75iNnmeC1A==,type:str] token: ENC[AES256_GCM,data:+y5ZOxfLAV9cg2l6XAh6odPjLgiKiQ/VqnEkhSRhi5+hdgkUvhSQ/MVjWeCvJQ==,iv:SzTU8F3uo2HTI2viFrkdtDK80tvf8LsgN4iftGv7XME=,tag:z3dQH8lwAvzc75iNnmeC1A==,type:str]
gpg: gpg:
forgejo-actions-bot: forgejo-actions-bot:
@ -44,6 +60,7 @@ postgresql:
openvpn: ENC[AES256_GCM,data:7c0oz6EiliNnGtV71CTYlTnbXk8PjgRaq2b07iqiBv5w9YT+66+x67IKXGyJNcjCEVY=,iv:j2oVv0qLzKdDZgLfXXps3wofSe+ofJMUEk73+I0eZJ8=,tag:h+CqC1qjlJUKYaO3SKwX4g==,type:str] openvpn: ENC[AES256_GCM,data:7c0oz6EiliNnGtV71CTYlTnbXk8PjgRaq2b07iqiBv5w9YT+66+x67IKXGyJNcjCEVY=,iv:j2oVv0qLzKdDZgLfXXps3wofSe+ofJMUEk73+I0eZJ8=,tag:h+CqC1qjlJUKYaO3SKwX4g==,type:str]
pypi: ENC[AES256_GCM,data:nfq3VL34oPQLCblDIZcYuSae9aeT918jDas3dexmR+VJOQLa4tN2x8HZTEw2mhz4Rh8MWTF4nmsii97rrngjz/C4d0Bz0b7hEKLIQIiAEs1rPJUKvbXDKZEUOz4/ujNBV/iwnwLq/P6Bj1hItK5GT1ofcydLXG7DF5CX2FFi5nohK8v1Uts1TVNH4XioZ9m6S42tsFnKYl1x3dcvliJl73nUghhitCtr0PcY66D4x0a9iaM=,iv:WCDrnLmjkKoiMcL9+vYEfeC5NA/IjTLjExvgKipOqZM=,tag:OoXoGEk4sQWfADc+07OiZg==,type:str] pypi: ENC[AES256_GCM,data:nfq3VL34oPQLCblDIZcYuSae9aeT918jDas3dexmR+VJOQLa4tN2x8HZTEw2mhz4Rh8MWTF4nmsii97rrngjz/C4d0Bz0b7hEKLIQIiAEs1rPJUKvbXDKZEUOz4/ujNBV/iwnwLq/P6Bj1hItK5GT1ofcydLXG7DF5CX2FFi5nohK8v1Uts1TVNH4XioZ9m6S42tsFnKYl1x3dcvliJl73nUghhitCtr0PcY66D4x0a9iaM=,iv:WCDrnLmjkKoiMcL9+vYEfeC5NA/IjTLjExvgKipOqZM=,tag:OoXoGEk4sQWfADc+07OiZg==,type:str]
qui: ENC[AES256_GCM,data:b4fhhwUmM/ZWDGOAKwd+Z/tf5dpTrbTaXIpbHAyqg2edGcL/XU3fKHAg6h4q4wkzBTRGRkaQUxScGRtCusPwSQ==,iv:P4nhMIwIsmzImcpVZnBC+zFVXvegIODdXZtiMDn+E5k=,tag:XCQWeEpbM1VIhwWVTieVyw==,type:str] qui: ENC[AES256_GCM,data:b4fhhwUmM/ZWDGOAKwd+Z/tf5dpTrbTaXIpbHAyqg2edGcL/XU3fKHAg6h4q4wkzBTRGRkaQUxScGRtCusPwSQ==,iv:P4nhMIwIsmzImcpVZnBC+zFVXvegIODdXZtiMDn+E5k=,tag:XCQWeEpbM1VIhwWVTieVyw==,type:str]
samba: ENC[AES256_GCM,data:ooBr/u/kYmwrIq2sE2b1nHuKUi/+f7hufNHXGeJ1+Gkxrf1bnnC44/Y4Qlwo,iv:2sz5oEZ65JvqYUMJuQqAA9BlqNpb+bj+dIgVYD2WUOk=,tag:X/0HLAWHI26Hd5BgHUjKLQ==,type:str]
rustypaste: rustypaste:
auth: ENC[AES256_GCM,data:fnBLzT/j2y3LnKLiOEB9nL7h+24Lhv58,iv:KiuNZFRDpD3LRD93kbi5Z7Y9xmFc4j6dCN1uYBo70dY=,tag:WArXhez94DTqi1nDptfhpQ==,type:str] auth: ENC[AES256_GCM,data:fnBLzT/j2y3LnKLiOEB9nL7h+24Lhv58,iv:KiuNZFRDpD3LRD93kbi5Z7Y9xmFc4j6dCN1uYBo70dY=,tag:WArXhez94DTqi1nDptfhpQ==,type:str]
delete: ENC[AES256_GCM,data:9IoGDlnQUpyPPq7l1LqMEpuWc+pS+BtV,iv:GAYm0rQtv02cNcmfLxXM8TqFNGKOX/fwcqlHBc5wbOE=,tag:XRdUT9SLZq1NII/xvw6fcg==,type:str] delete: ENC[AES256_GCM,data:9IoGDlnQUpyPPq7l1LqMEpuWc+pS+BtV,iv:GAYm0rQtv02cNcmfLxXM8TqFNGKOX/fwcqlHBc5wbOE=,tag:XRdUT9SLZq1NII/xvw6fcg==,type:str]
@ -58,7 +75,7 @@ tailscale:
auth: auth:
machines: ENC[AES256_GCM,data:6Air2d84iDya1T7iUnqb14hunsAU4fJzidbY0D2DvpmffwpfjFV2pcldUOJ3t60BOULGRbUTulL1raJs2Q==,iv:LoBO1FNWorBLVYoiD0ZGJjiqzTxa21TqQQBHCgzjwcg=,tag:Pu/Xxk8/wnPWQX6m12LERQ==,type:str] machines: ENC[AES256_GCM,data:6Air2d84iDya1T7iUnqb14hunsAU4fJzidbY0D2DvpmffwpfjFV2pcldUOJ3t60BOULGRbUTulL1raJs2Q==,iv:LoBO1FNWorBLVYoiD0ZGJjiqzTxa21TqQQBHCgzjwcg=,tag:Pu/Xxk8/wnPWQX6m12LERQ==,type:str]
tsdproxy: ENC[AES256_GCM,data:kplbdX890oTi/c4/LC1I9NdgpkpIrttdryd1CslpDPsmL/z3QnYJg9hIq9buiKS7AidfmD+h/mvdQ8UoVdc=,iv:4tAI96zOJj+q0eyxuFBqEtz0NDSCvFcqmRbPcxOACdM=,tag:rXK493h+d6tTc0KSgeQKTw==,type:str] tsdproxy: ENC[AES256_GCM,data:kplbdX890oTi/c4/LC1I9NdgpkpIrttdryd1CslpDPsmL/z3QnYJg9hIq9buiKS7AidfmD+h/mvdQ8UoVdc=,iv:4tAI96zOJj+q0eyxuFBqEtz0NDSCvFcqmRbPcxOACdM=,tag:rXK493h+d6tTc0KSgeQKTw==,type:str]
vaultwarden: ENC[AES256_GCM,data:hXtsApGe3Htj6agKsgKG4aM7Y+aDe6AFzCzTE5+CoXSYRlWE6yhe0dMRZ5GYGwVV1Jcy9/9LvEksxhbP2sAgTW7pu6fa/kcn/IjoJm+Lqbg4F0rzruB56HxKypFblqqW2sq5gr3REEFU6y1SKN+eyyV0JM2hfTcb+SpbEPf+s2KhzADo,iv:04NeSQonfUTZ3zzp9lcbXGtILDpxaQVSoWWzFuGsWvA=,tag:Xn2UHGsq3J3Trbll6aqXTQ==,type:str] vaultwarden: ENC[AES256_GCM,data:Fpxktv+jbY7/K3p+0sMCWpVJUUZVonvll6Dpm9eD7eXUi7DmkpMlxDcq5nZHhlE1t0FeAf7y9cQVEopgKMkzJ1wCAfUe4Umd77p8N0hWyfbkWsoYex98vuS/pRcDWAoQIQgYmaigqXzG4KMj2Ya1qL/lo2+OEbtmzLqCi5cLfgwEL9XhZVR4DMB1QKnMeQixY+w3UAi+yDfoqStaSiIyuYhIIeCrK6zLHl3QYZXZrHDBRZXt2M5e3ic9riIv,iv:iXZX85fhy1Wr2q2RAwVLzvudlcsHHe+MIY6dcni53ME=,tag:BBwdyTdp/8Xaz4/jWu75fw==,type:str]
woodpecker: woodpecker:
forgejo: forgejo:
client: ENC[AES256_GCM,data:ThYDCeaGAYcPuuM+SPTq0axYoMC2xyiM5Bigp5peTOAcoUd5,iv:WFIYKs915cYg7GCs0VfCS+/PQT3ATwJIROWh1Pt+Zew=,tag:G9T2qkW8icnfFyy3H0F1sA==,type:str] client: ENC[AES256_GCM,data:ThYDCeaGAYcPuuM+SPTq0axYoMC2xyiM5Bigp5peTOAcoUd5,iv:WFIYKs915cYg7GCs0VfCS+/PQT3ATwJIROWh1Pt+Zew=,tag:G9T2qkW8icnfFyy3H0F1sA==,type:str]
@ -151,7 +168,7 @@ sops:
yUtR1lCjpW5Hpi2vDoom4HDsodeXfazqS+RjpHuKOLeO39bb4KbAhg== yUtR1lCjpW5Hpi2vDoom4HDsodeXfazqS+RjpHuKOLeO39bb4KbAhg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1988jz0qfj6m9xfenhd292tkxwyxvzhv79gkgrhm0gep353kej3yqe6zp86 recipient: age1988jz0qfj6m9xfenhd292tkxwyxvzhv79gkgrhm0gep353kej3yqe6zp86
lastmodified: "2026-06-30T02:30:43Z" lastmodified: "2026-06-30T19:23:37Z"
mac: ENC[AES256_GCM,data:gVelQq9dD9WA2Bdf63VVvofsuS8p/r0Vknc0HbGUHxeHvDalZT6JG0eo5tYHpjTFynqdEr0nNt2P9FC7fLi3cG01l9kO/AEAQKJA4JajisJMaFvhxCZUQKT3Utjh+boHn99Ni41ocNV4+kt5ro1Wm0eZCStleem2C4J1AkoBorM=,iv:mpRvcmm7vCoDuRYolbSOtlyH+EjLdBLiYmDBUFWXgb4=,tag:61eemKlhp5G1vx36TGvAAQ==,type:str] mac: ENC[AES256_GCM,data:FewAMhmopqxq6N6elvjIVmyR+LaQCh/ItQFVU0tSUInp6+qz6jPb1gKYqOI0pOU0C5euBV02K39afY8vrJBc8hM7AD+talKGXmgNULX3f4JWOnNrfOgFhgoqXTgWrU+kAEHmM5ZNlFICYeEow6i+/t6DAlVFuylrCpFokJLH0bU=,iv:7QnAu29thDRu/AamlvyjgZp4MwoiScELDByLSDWTjV0=,tag:y++WaY41XP/ifUo7IqKZZw==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.13.1 version: 3.13.1