6 Commits

13 changed files with 418 additions and 98 deletions
Generated
+67 -67
View File
@@ -79,11 +79,11 @@
"agents_3": { "agents_3": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1777399938, "lastModified": 1778518220,
"narHash": "sha256-xXPqUQezDdDtF8MbpZnwD1HkybOYwF92evx8rJ6OXCU=", "narHash": "sha256-6AQs9VZ0/DuD4njPbYHRE4v+SgJc6SBrGwemTWxikVc=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "9a91f1ee0cf011a7eaf1f16a9e17610b0457e055", "rev": "b6e1aaa6261c5056d024d8d4785659eaa4e675e6",
"revCount": 85, "revCount": 87,
"type": "git", "type": "git",
"url": "https://code.m3ta.dev/m3tam3re/AGENTS" "url": "https://code.m3ta.dev/m3tam3re/AGENTS"
}, },
@@ -214,11 +214,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1777369708, "lastModified": 1778445566,
"narHash": "sha256-1xW7cRZNsFNPQD+cE0fwnLVStnDth0HSoASEIFeT7uI=", "narHash": "sha256-oQvcadh2BCkrog+SGrG6YffKJrveYpjj3TdQJWaKhaM=",
"owner": "nix-community", "owner": "nix-community",
"repo": "bun2nix", "repo": "bun2nix",
"rev": "e659e1cc4b8e1b21d0aa85f1c481f9db61ecfa98", "rev": "2499dedd70744dba1815875b854818a3019e9e4c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -280,11 +280,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1777713215, "lastModified": 1778958912,
"narHash": "sha256-8GzXDOXckDWwST8TY5DbwYFjdvQLlP7K9CLSVx6iTTo=", "narHash": "sha256-6pvS9rIF9mZRj1ENwu9fDLHeG1JFDTCpRyy6vJhXkTA=",
"owner": "nix-community", "owner": "nix-community",
"repo": "disko", "repo": "disko",
"rev": "63b4e7e6cf75307c1d26ac3762b886b5b0247267", "rev": "6e8dc7aa0e65fce67c76e18227a13a7d529f2cdf",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -322,11 +322,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1777988971, "lastModified": 1778716662,
"narHash": "sha256-qIoWPDs+0/8JecyYgE3gpKQxW/4bLW/gp45vow9ioCQ=", "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "0678d8986be1661af6bb555f3489f2fdfc31f6ff", "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -406,16 +406,16 @@
"uv2nix": "uv2nix_2" "uv2nix": "uv2nix_2"
}, },
"locked": { "locked": {
"lastModified": 1778170968, "lastModified": 1778925537,
"narHash": "sha256-YQQUEDUim2CiYpL3uG7Wi1fWPsT2wtIqoBeJuAj9hUk=", "narHash": "sha256-d9qhrTy45Q5UsmjapqMHOVi9e+gR9zE8Nq9Z0wObLmc=",
"owner": "NousResearch", "owner": "NousResearch",
"repo": "hermes-agent", "repo": "hermes-agent",
"rev": "498bfc7bc12a937621b4215312049b1000726df3", "rev": "a91a57fa5a13d516c38b07a141a9ce8a3daabeb0",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NousResearch", "owner": "NousResearch",
"ref": "v2026.5.7", "ref": "v2026.5.16",
"repo": "hermes-agent", "repo": "hermes-agent",
"type": "github" "type": "github"
} }
@@ -448,11 +448,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1778248595, "lastModified": 1779125151,
"narHash": "sha256-dhFgEjoeJMYN/7OY6xfxS799YB4IjbbYXTjyGIJyLpc=", "narHash": "sha256-bPHT0oJAHe5a43lkkSGAiCEg/RBqpwv5xsFrcj+Bog8=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "fdb2ccba9d5e1238d32e0c4a3ec1a277efa80c1d", "rev": "fab3fd7327a0ac7a1fae5095bf140377704fac7f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -577,11 +577,11 @@
"treefmt-nix": "treefmt-nix" "treefmt-nix": "treefmt-nix"
}, },
"locked": { "locked": {
"lastModified": 1778304612, "lastModified": 1779082717,
"narHash": "sha256-7FkBnR56KZ8RwY5kPd3ans8f68IYjF1J66gOUlLsiLA=", "narHash": "sha256-TE2ynGDxq6ahXlzxCDfatOYcnvvsOIi/QTMIZS0gWq0=",
"owner": "numtide", "owner": "numtide",
"repo": "llm-agents.nix", "repo": "llm-agents.nix",
"rev": "c741913095c4815f6651aa0a2c24b3ce15e414e4", "rev": "1f1ede7969673edd1d35764f5c930ecf96487156",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -602,11 +602,11 @@
"nur": "nur" "nur": "nur"
}, },
"locked": { "locked": {
"lastModified": 1778520138, "lastModified": 1779128125,
"narHash": "sha256-X58c8BUIshyUnp6XEKumFUYXqMFnrDTj+aGuGIbKwxg=", "narHash": "sha256-Lt/n7O42uAZrgjoDttgVlfjauiyE91+X8XDlIKS0twM=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "a87d9510bd84f51bf93970730b8688ab7221bbdd", "rev": "70562ceef2bf9508fbfcea13f96c073602530cf1",
"revCount": 30, "revCount": 33,
"type": "git", "type": "git",
"url": "ssh://gitea@code.m3ta.dev/m3tam3re/m3ta-home" "url": "ssh://gitea@code.m3ta.dev/m3tam3re/m3ta-home"
}, },
@@ -649,11 +649,11 @@
"openspec": "openspec_2" "openspec": "openspec_2"
}, },
"locked": { "locked": {
"lastModified": 1778518789, "lastModified": 1779112891,
"narHash": "sha256-9WZvO2BBofC2Wp4dvP4/aQ6Jhmcxh9lEGTYj09hLXrI=", "narHash": "sha256-UtRPNT1Pn2H42h2zc0GuyWi08wH6g00Mkth/bnuXu/Y=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "d64c581516c02702ec28e5d2304330d7b035235d", "rev": "f265aaff108496e835fcd318d5c850d8b49cbb73",
"revCount": 295, "revCount": 309,
"type": "git", "type": "git",
"url": "ssh://gitea@code.m3ta.dev/m3tam3re/nixpkgs" "url": "ssh://gitea@code.m3ta.dev/m3tam3re/nixpkgs"
}, },
@@ -846,11 +846,11 @@
}, },
"nixpkgs-master_2": { "nixpkgs-master_2": {
"locked": { "locked": {
"lastModified": 1778507606, "lastModified": 1779112318,
"narHash": "sha256-6Yc2dIhijc8G+dbMNocyclxF19dUrjaT+EeXGrXmXlg=", "narHash": "sha256-nuEcdfdbqAymI+Fgbw5YruK/vv1vbLo899I3rx+k5fw=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "39a7b8d815fcc8b689d56fc4a3fa8de4ef93d169", "rev": "caf7b95c65a9f0a94cad75dbf2ee2650286111fc",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -862,11 +862,11 @@
}, },
"nixpkgs-master_3": { "nixpkgs-master_3": {
"locked": { "locked": {
"lastModified": 1778307931, "lastModified": 1779127438,
"narHash": "sha256-GkUOqeH6tb2/K1tv3t0F/xROIAh5/zEGutzEUIrQ+u8=", "narHash": "sha256-2dTsb4EIQw6WNS+zGFij2Io8ic4eHZAEbYY59d9pcyg=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "8f689324e32c31a3d2c24490a19e266c3fb6508b", "rev": "559847f92aee8e4bcb24411bc23bca1b94a8595f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -878,11 +878,11 @@
}, },
"nixpkgs-stable": { "nixpkgs-stable": {
"locked": { "locked": {
"lastModified": 1778003029, "lastModified": 1778737229,
"narHash": "sha256-q/nkKLDtHIyLjZpKhWk3cSK5IYsFqtMd6UtXF3ddjgA=", "narHash": "sha256-6xWoytx8jFW4PF1GjRm/i/53trbpKGfz6zjzQGBr4cI=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "0c88e1f2bdb93d5999019e99cb0e61e1fe2af4c5", "rev": "d7a713c0b7e47c908258e71cba7a2d77cc8d71d5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -894,11 +894,11 @@
}, },
"nixpkgs_10": { "nixpkgs_10": {
"locked": { "locked": {
"lastModified": 1777954456, "lastModified": 1778869304,
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", "rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -974,11 +974,11 @@
}, },
"nixpkgs_5": { "nixpkgs_5": {
"locked": { "locked": {
"lastModified": 1778124196, "lastModified": 1778869304,
"narHash": "sha256-pYEytCNic/czazbV9r3tbQ6BZzqRBg/41x2dIC5ymOo=", "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "68a8af93ff4297686cb68880845e61e5e2e41d92", "rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1022,11 +1022,11 @@
}, },
"nixpkgs_8": { "nixpkgs_8": {
"locked": { "locked": {
"lastModified": 1777954456, "lastModified": 1778869304,
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", "rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1100,11 +1100,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1778308643, "lastModified": 1779124721,
"narHash": "sha256-MpJyLyJWAwOy7rVs7pWqRwH2b8/rj+B524VzdonvXBs=", "narHash": "sha256-Z1q8QkuHAdkmXh4SItOzViVVFVLb+tzaEaYCTHI2UKk=",
"owner": "nix-community", "owner": "nix-community",
"repo": "NUR", "repo": "NUR",
"rev": "98d908741ed91101cd0649961f12d2427bdba7d3", "rev": "ad57d8faf36bb02c65c90012cd0289daa0b2d9c4",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1143,11 +1143,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1778120451, "lastModified": 1778774456,
"narHash": "sha256-MUSPD16+hoFBfQWYahtNLN2BIFEAlFFo2KNofrc947g=", "narHash": "sha256-4V35mdLWax+GfuUK6hv2Vgri6N/vAJApjuCB3ROOY6w=",
"owner": "Fission-AI", "owner": "Fission-AI",
"repo": "OpenSpec", "repo": "OpenSpec",
"rev": "053d8a59d587f3c027a06ad80503a6b43d4f2a92", "rev": "8498042fe8a738e8ad6facd94a5fc7f5025bf81d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1316,11 +1316,11 @@
"skills-anthropic": { "skills-anthropic": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1778286877, "lastModified": 1779058037,
"narHash": "sha256-jKNYFom6R+Qw7LQ8vFPBe51JpqIP0tTSY8LM4aPlnT4=", "narHash": "sha256-GytrPFxw1PC2B0MILR6eNa83qAmxcjvLPkJzHQXT93g=",
"owner": "anthropics", "owner": "anthropics",
"repo": "skills", "repo": "skills",
"rev": "f458cee31a7577a47ba0c9a101976fa599385174", "rev": "6a5bb06904ab164a345e41c381fc9097954b83da",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1332,11 +1332,11 @@
"skills-basecamp": { "skills-basecamp": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1777902228, "lastModified": 1778520277,
"narHash": "sha256-XDsWpUhFb/gxatRFla07nwoc2y3WwaBLsiDdtCnqx38=", "narHash": "sha256-gaV9eIIzOTBlL+9+e8HIgCs4pa1J8lAizRykkJVoVUM=",
"owner": "basecamp", "owner": "basecamp",
"repo": "basecamp-cli", "repo": "basecamp-cli",
"rev": "b56ada1b3d42b42a9422ba39b30a223f9f960231", "rev": "f948edca1dbce53640056743bf49f05cf39e736b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1348,11 +1348,11 @@
"skills-kestra": { "skills-kestra": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1773046826, "lastModified": 1778585472,
"narHash": "sha256-w1zFqfCAcu9FsaGf8uAyaaYVbSwwtUzotfDJ1jSt+q0=", "narHash": "sha256-jtK4wwLE4y4vsnonSmIhlAIJ/g0zqPDt3TO+Frb6LEU=",
"owner": "kestra-io", "owner": "kestra-io",
"repo": "agent-skills", "repo": "agent-skills",
"rev": "b536825bf5b9213d7a7fb5ab7c47823f1044490b", "rev": "cc9cd71fbada02f8ac22a1f3ae7ad5e7242bda45",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1380,11 +1380,11 @@
"skills-vercel": { "skills-vercel": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1778275952, "lastModified": 1778774027,
"narHash": "sha256-RYwgUf173N4lGalTta4HkBR7sdZwuzRoAY6M8JsT+RY=", "narHash": "sha256-Dzp0Gx+EcO7daxLTZ0QpMu4EEYdDWWEE8b5RF4Fv9QM=",
"owner": "vercel-labs", "owner": "vercel-labs",
"repo": "skills", "repo": "skills",
"rev": "c99a72b371b5b4da865f5afa87c5a686f3a46766", "rev": "c5ad3a85b4d16666974b161131413d08bfef3f7e",
"type": "github" "type": "github"
}, },
"original": { "original": {
+1 -1
View File
@@ -73,7 +73,7 @@
url = "github:vercel-labs/skills"; url = "github:vercel-labs/skills";
flake = false; flake = false;
}; };
hermes-agent.url = "github:NousResearch/hermes-agent/v2026.5.7"; hermes-agent.url = "github:NousResearch/hermes-agent/v2026.5.16";
rustfs = { rustfs = {
url = "github:rustfs/rustfs-flake"; url = "github:rustfs/rustfs-flake";
+5
View File
@@ -39,6 +39,11 @@
outline = 3019; outline = 3019;
authentik = 3023; authentik = 3023;
tuwunel = 3024; tuwunel = 3024;
honcho = 3025;
# Agent infrastructure
hermes-api = 8642;
hermes-dashboard = 9119;
# Home automation # Home automation
homarr = 7575; homarr = 7575;
+8
View File
@@ -3,6 +3,14 @@
secrets = { secrets = {
baserow-env = {file = ../../secrets/baserow-env.age;}; baserow-env = {file = ../../secrets/baserow-env.age;};
ghost-env = {file = ../../secrets/ghost-env.age;}; ghost-env = {file = ../../secrets/ghost-env.age;};
honcho-selfhost-db-password = {
file = ../../secrets/honcho-selfhost-db-password.age;
owner = "postgres";
group = "postgres";
mode = "400";
};
honcho-selfhost-env = {file = ../../secrets/honcho-selfhost-env.age;};
honcho-selfhost-jwt-secret = {file = ../../secrets/honcho-selfhost-jwt-secret.age;};
kestra-config = { kestra-config = {
file = ../../secrets/kestra-config.age; file = ../../secrets/kestra-config.age;
mode = "644"; mode = "644";
@@ -2,6 +2,7 @@
imports = [ imports = [
./baserow.nix ./baserow.nix
./ghost.nix ./ghost.nix
./honcho.nix
./kestra.nix ./kestra.nix
./littlelink.nix ./littlelink.nix
./matomo.nix ./matomo.nix
@@ -0,0 +1,209 @@
{
config,
lib,
pkgs,
...
}: let
serviceName = "honcho";
image = "ghcr.io/plastic-labs/honcho:v3.0.6";
apiIp = "10.89.0.24";
deriverIp = "10.89.0.25";
redisIp = "10.89.0.26";
postgresHost = "10.89.0.1";
postgresPort = config.m3ta.ports.get "postgres";
honchoPort = config.m3ta.ports.get "honcho";
# m3-atlas Netbird mesh address, discovered from `netbird status -d`.
# Binding the host port here keeps self-hosted Honcho off public interfaces.
netbirdBindAddress = "100.81.142.56";
netbirdRange = "100.64.0.0/16";
dbName = "honcho";
dbUser = "honcho";
redisName = "${serviceName}-redis";
runtimeDirectory = "/run/${serviceName}";
runtimeEnvFile = "${runtimeDirectory}/env";
# Keep auth disabled for the first deployment because Honcho clients need
# generated JWTs. The JWT secret is still provisioned so enabling auth later is
# a one-line change here plus client token generation.
authUseAuth = false;
sharedEnvironment = {
CACHE_ENABLED = "true";
CACHE_URL = "redis://${redisName}:6379/0?suppress=true";
LOG_LEVEL = "INFO";
TELEMETRY_ENABLED = "false";
VECTOR_STORE_MIGRATED = "false";
VECTOR_STORE_TYPE = "pgvector";
AUTH_USE_AUTH = lib.boolToString authUseAuth;
};
sharedEnvironmentFiles = [
runtimeEnvFile
config.age.secrets."${serviceName}-selfhost-env".path
];
webNetwork = ip: [
"--add-host=postgres:${postgresHost}"
"--network=web:ip=${ip}"
];
# The shared web network is intentionally internal. API and deriver also join
# this egress-only network so LLM provider calls can leave the host without
# exposing any extra inbound ports.
networksWithEgress = ip:
(webNetwork ip)
++ [
"--network=${serviceName}-egress"
];
apiHealthCmd = ''/app/.venv/bin/python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=2).read()"'';
in {
system.activationScripts.createPodmanNetworkHonchoEgress = lib.mkAfter ''
if ! /run/current-system/sw/bin/podman network exists ${serviceName}-egress; then
/run/current-system/sw/bin/podman network create ${serviceName}-egress
fi
'';
virtualisation.oci-containers.containers = {
"${serviceName}-redis" = {
image = "docker.io/redis:8.2";
autoStart = true;
volumes = ["${serviceName}_redis_data:/data"];
extraOptions =
(webNetwork redisIp)
++ [
"--health-cmd=redis-cli ping"
"--health-interval=5s"
"--health-timeout=5s"
"--health-retries=5"
];
};
"${serviceName}-api" = {
inherit image;
autoStart = true;
entrypoint = "sh";
cmd = ["docker/entrypoint.sh"];
environment = sharedEnvironment;
environmentFiles = sharedEnvironmentFiles;
ports = ["${netbirdBindAddress}:${toString honchoPort}:8000"];
dependsOn = [redisName];
extraOptions =
(networksWithEgress apiIp)
++ [
"--health-cmd=${apiHealthCmd}"
"--health-interval=5s"
"--health-timeout=5s"
"--health-retries=5"
"--health-start-period=10s"
];
};
"${serviceName}-deriver" = {
inherit image;
autoStart = true;
entrypoint = "/app/.venv/bin/python";
cmd = ["-m" "src.deriver"];
environment = sharedEnvironment;
environmentFiles = sharedEnvironmentFiles;
dependsOn = ["${serviceName}-api" redisName];
extraOptions = networksWithEgress deriverIp;
};
};
systemd.services = {
"${serviceName}-postgres-bootstrap" = {
description = "Bootstrap Honcho PostgreSQL role, database, password, and pgvector";
after = ["postgresql.service" "agenix.service"];
requires = ["postgresql.service" "agenix.service"];
before = ["${serviceName}-env.service" "podman-${serviceName}-api.service" "podman-${serviceName}-deriver.service"];
requiredBy = ["podman-${serviceName}-api.service" "podman-${serviceName}-deriver.service"];
path = [
config.services.postgresql.package
pkgs.coreutils
];
serviceConfig = {
Type = "oneshot";
User = "postgres";
Group = "postgres";
};
script = ''
set -euo pipefail
test -s ${config.age.secrets."${serviceName}-selfhost-db-password".path}
psql -v ON_ERROR_STOP=1 --dbname=postgres <<'SQL'
DO $$
BEGIN
CREATE ROLE ${dbUser} LOGIN;
EXCEPTION WHEN duplicate_object THEN
NULL;
END
$$;
SELECT 'CREATE DATABASE ${dbName} OWNER ${dbUser}'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${dbName}')\gexec
ALTER DATABASE ${dbName} OWNER TO ${dbUser};
\set honcho_password `cat ${config.age.secrets."${serviceName}-selfhost-db-password".path}`
ALTER ROLE ${dbUser} WITH LOGIN PASSWORD :'honcho_password';
SQL
psql -v ON_ERROR_STOP=1 --dbname=${dbName} <<'SQL'
CREATE EXTENSION IF NOT EXISTS vector;
GRANT ALL PRIVILEGES ON DATABASE ${dbName} TO ${dbUser};
SQL
'';
};
"${serviceName}-env" = {
description = "Generate Honcho runtime environment file with agenix secrets";
after = ["agenix.service" "${serviceName}-postgres-bootstrap.service"];
requires = ["agenix.service" "${serviceName}-postgres-bootstrap.service"];
before = ["podman-${serviceName}-api.service" "podman-${serviceName}-deriver.service"];
requiredBy = ["podman-${serviceName}-api.service" "podman-${serviceName}-deriver.service"];
path = [
pkgs.coreutils
pkgs.python3
];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
set -euo pipefail
install -d -m 0750 ${runtimeDirectory}
db_password_encoded=$(
python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip(), safe=""))' \
< ${config.age.secrets."${serviceName}-selfhost-db-password".path}
)
jwt_secret=$(tr -d '\r\n' < ${config.age.secrets."${serviceName}-selfhost-jwt-secret".path})
umask 077
cat > ${runtimeEnvFile} <<ENV
DB_CONNECTION_URI=postgresql+psycopg://${dbUser}:$db_password_encoded@postgres:${toString postgresPort}/${dbName}
AUTH_JWT_SECRET=$jwt_secret
ENV
'';
};
"podman-${serviceName}-api" = {
after = ["${serviceName}-env.service" "${serviceName}-postgres-bootstrap.service"];
requires = ["${serviceName}-env.service" "${serviceName}-postgres-bootstrap.service"];
};
"podman-${serviceName}-deriver" = {
after = ["${serviceName}-env.service" "${serviceName}-postgres-bootstrap.service"];
requires = ["${serviceName}-env.service" "${serviceName}-postgres-bootstrap.service"];
};
};
networking.firewall.extraCommands = ''
# Self-hosted Honcho API: only Netbird mesh peers may reach ${netbirdBindAddress}:${toString honchoPort}.
ip46tables -A nixos-fw -p tcp --dport ${toString honchoPort} -s ${netbirdRange} -j nixos-fw-accept
'';
}
+2 -1
View File
@@ -28,6 +28,7 @@
host kestra kestra 10.89.0.0/24 scram-sha-256 host kestra kestra 10.89.0.0/24 scram-sha-256
host netbird netbird 10.89.0.0/24 scram-sha-256 host netbird netbird 10.89.0.0/24 scram-sha-256
host authentik authentik 10.89.0.0/24 scram-sha-256 host authentik authentik 10.89.0.0/24 scram-sha-256
host honcho honcho 10.89.0.0/24 scram-sha-256
# Deny all other connections # Deny all other connections
local all all reject local all all reject
@@ -38,7 +39,7 @@
services.postgresqlBackup = { services.postgresqlBackup = {
enable = true; enable = true;
startAt = "03:10:00"; startAt = "03:10:00";
databases = ["baserow" "paperless" "kestra" "authentik" "netbird"]; databases = ["baserow" "paperless" "kestra" "authentik" "netbird" "honcho"];
}; };
networking.firewall = { networking.firewall = {
extraCommands = '' extraCommands = ''
+19 -22
View File
@@ -7,14 +7,6 @@
# Edge TTS: Seraphina — friendly, multilingual German female voice (free, no API key) # Edge TTS: Seraphina — friendly, multilingual German female voice (free, no API key)
edgeVoice = "de-DE-SeraphinaMultilingualNeural"; edgeVoice = "de-DE-SeraphinaMultilingualNeural";
# Extra Python packages from the container's writable venv layer.
# matrix-nio is installed via pip in /home/hermes/.venv but the hermes
# process uses the read-only Nix store Python, so we inject the venv's
# site-packages via PYTHONPATH and provide libstdc++ for libolm (e2e).
# NOTE: v0.13.0 upgraded to Python 3.12 — path updated accordingly.
venvSitePackages = "/home/hermes/.venv/lib/python3.12/site-packages";
gccLibPath = "${pkgs.stdenv.cc.cc.lib}/lib";
# Build skills using agents flake lib for hermes user # Build skills using agents flake lib for hermes user
hermesSkills = inputs.agents.lib.mkSkills { hermesSkills = inputs.agents.lib.mkSkills {
inherit pkgs; inherit pkgs;
@@ -38,9 +30,9 @@ in {
virtualisation.docker.enable = true; virtualisation.docker.enable = true;
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d /home/hermes/.config 0755 hermes hermes -" "d /var/lib/hermes/.config 0755 hermes hermes -"
"d /home/hermes/.config/tea 0755 hermes hermes -" "d /var/lib/hermes/.config/tea 0755 hermes hermes -"
"L+ /home/hermes/.config/tea/yml - - - - ${pkgs.writeText "tea-yml" '' "L+ /var/lib/hermes/.config/tea/yml - - - - ${pkgs.writeText "tea-yml" ''
logins: logins:
- name: m3ta - name: m3ta
url: https://code.m3ta.dev url: https://code.m3ta.dev
@@ -64,19 +56,29 @@ in {
''; '';
}; };
# Ensure 'uv' is in the hermes-agent service PATH so CronJobs and terminal
# sessions can use 'uv run' for PEP 723 scripts (e.g. garmin-daily.py).
systemd.services.hermes-agent.path = [pkgs.uv];
services.hermes-agent = { services.hermes-agent = {
enable = true; enable = true;
addToSystemPackages = true; addToSystemPackages = true;
# v0.14 lazy-installs heavy optional backends by default. In the sealed
# Nix package, include the backends this host config actively uses so the
# gateway, Matrix bridge, memory, web search, and TTS work
# without runtime pip/uv mutation.
extraDependencyGroups = [
"matrix"
"honcho"
"exa"
"edge-tts"
];
extraPackages = with pkgs; [ extraPackages = with pkgs; [
docker docker
git git
curl
jq
tea tea
nix nix
python3Minimal
uv
zellij zellij
]; ];
@@ -105,7 +107,7 @@ in {
# Bind to 0.0.0.0 so the Netbird interface can reach it. # Bind to 0.0.0.0 so the Netbird interface can reach it.
API_SERVER_ENABLED = "true"; API_SERVER_ENABLED = "true";
API_SERVER_HOST = "0.0.0.0"; API_SERVER_HOST = "0.0.0.0";
API_SERVER_PORT = "8642"; API_SERVER_PORT = toString (config.m3ta.ports.get "hermes-api");
}; };
# ── Container mode (podman) ────────────────────────────────────────── # ── Container mode (podman) ──────────────────────────────────────────
@@ -113,12 +115,7 @@ in {
enable = false; enable = false;
backend = "podman"; backend = "podman";
extraVolumes = ["/home/m3tam3re/p:/projects:rw"]; extraVolumes = ["/home/m3tam3re/p:/projects:rw"];
extraOptions = [ extraOptions = [];
"--env"
"PYTHONPATH=${venvSitePackages}"
"--env"
"LD_LIBRARY_PATH=${gccLibPath}"
];
}; };
settings = { settings = {
+11 -7
View File
@@ -7,6 +7,8 @@
# Netbird mesh VPN range — dashboard only accessible from mesh peers. # Netbird mesh VPN range — dashboard only accessible from mesh peers.
# m3-atlas Traefik proxies to this port over Netbird. # m3-atlas Traefik proxies to this port over Netbird.
netbirdRange = "100.64.0.0/16"; netbirdRange = "100.64.0.0/16";
apiPort = config.m3ta.ports.get "hermes-api";
dashboardPort = config.m3ta.ports.get "hermes-dashboard";
# Reference the hermes-agent package from the running service config # Reference the hermes-agent package from the running service config
hermesPkg = config.services.hermes-agent.package or (inputs.hermes-agent.packages.${pkgs.stdenv.hostPlatform.system}.default or pkgs.hermes-agent); hermesPkg = config.services.hermes-agent.package or (inputs.hermes-agent.packages.${pkgs.stdenv.hostPlatform.system}.default or pkgs.hermes-agent);
@@ -14,10 +16,10 @@ in {
# ── Hermes Dashboard systemd service ─────────────────────────────────── # ── Hermes Dashboard systemd service ───────────────────────────────────
# Web UI for managing Hermes Agent — sessions, config, kanban, cron, etc. # Web UI for managing Hermes Agent — sessions, config, kanban, cron, etc.
# #
# Flow: Browser → dash.m3ta.dev (TLS via m3-atlas Traefik) → Netbird → :9119 # Flow: Browser → dash.m3ta.dev (TLS via m3-atlas Traefik) → Netbird → :${toString dashboardPort}
# #
# --insecure is required to bind 0.0.0.0 (hermes refuses non-localhost otherwise). # --insecure is required to bind 0.0.0.0 (hermes refuses non-localhost otherwise).
# Safe because firewall restricts port 9119 to Netbird mesh only. # Safe because firewall restricts the dashboard/API ports to Netbird mesh only.
systemd.services.hermes-dashboard = { systemd.services.hermes-dashboard = {
description = "Hermes Agent Web Dashboard"; description = "Hermes Agent Web Dashboard";
after = ["network.target" "hermes-agent.service"]; after = ["network.target" "hermes-agent.service"];
@@ -29,7 +31,7 @@ in {
User = "hermes"; User = "hermes";
Group = "hermes"; Group = "hermes";
ExecStart = "${hermesPkg}/bin/hermes dashboard --host 0.0.0.0 --port 9119 --no-open --insecure"; ExecStart = "${hermesPkg}/bin/hermes dashboard --host 0.0.0.0 --port ${toString dashboardPort} --no-open --insecure";
# Environment matching the hermes-agent service # Environment matching the hermes-agent service
Environment = [ Environment = [
@@ -51,15 +53,17 @@ in {
}; };
}; };
# ── Firewall: Dashboard only from Netbird mesh ───────────────────────── # ── Firewall: Hermes network endpoints only from Netbird mesh ──────────
networking.firewall = { networking.firewall = {
extraCommands = '' extraCommands = ''
# Allow Hermes Dashboard (9119/tcp) only from Netbird mesh VPN # Allow Hermes Dashboard and OpenAI-compatible API only from Netbird mesh VPN
ip46tables -A nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept ip46tables -A nixos-fw -p tcp --dport ${toString dashboardPort} -s ${netbirdRange} -j nixos-fw-accept
ip46tables -A nixos-fw -p tcp --dport ${toString apiPort} -s ${netbirdRange} -j nixos-fw-accept
''; '';
extraStopCommands = '' extraStopCommands = ''
ip46tables -D nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true ip46tables -D nixos-fw -p tcp --dport ${toString dashboardPort} -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true
ip46tables -D nixos-fw -p tcp --dport ${toString apiPort} -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true
''; '';
}; };
} }
+3
View File
@@ -38,6 +38,9 @@ in {
"secrets/basecamp-client-id.age".publicKeys = systems ++ users; "secrets/basecamp-client-id.age".publicKeys = systems ++ users;
"secrets/basecamp-client-secret.age".publicKeys = systems ++ users; "secrets/basecamp-client-secret.age".publicKeys = systems ++ users;
"secrets/gitea-runner-token.age".publicKeys = systems ++ users; "secrets/gitea-runner-token.age".publicKeys = systems ++ users;
"secrets/honcho-selfhost-db-password.age".publicKeys = systems ++ users;
"secrets/honcho-selfhost-env.age".publicKeys = systems ++ users;
"secrets/honcho-selfhost-jwt-secret.age".publicKeys = systems ++ users;
"secrets/outline-key.age".publicKeys = systems ++ users; "secrets/outline-key.age".publicKeys = systems ++ users;
"secrets/restreamer-env.age".publicKeys = systems ++ users; "secrets/restreamer-env.age".publicKeys = systems ++ users;
"secrets/searx.age".publicKeys = systems ++ users; "secrets/searx.age".publicKeys = systems ++ users;
+31
View File
@@ -0,0 +1,31 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVrd2NzQSBJV1Fa
U2gzWjJKOHlsK202dXAzVENFdmZ5ZmJsc01STnRnTWMvdjFyZ21NCjU5Q0hwbVlV
dVlRTUZDb0JESHBaTzk5ancrTWNpLzYrMFk2QWU0Z0EvUmsKLT4gc3NoLWVkMjU1
MTkgM0JjcjF3IEdia0tSQTZWcEE3ODE4VjU2M0ZGay9rbXlzbFNqZWxpb1lGSExw
RW5keGsKWDlwRzcwT1BaTDByT0R6T3hqWTE1UWVyN2xhSzhSMlhBdW5FakNjZ1Ux
NAotPiBzc2gtZWQyNTUxOSA5ZDRZSVEgcnNJRnA2SVVkMHRXZTErc0VFbmduMDBz
ZkQzK3dMQ2xadkMyYllhOE1rOAprWmg4T3RiaTF5QXdmRGUxVDVpVnZWdWxRYTJv
WGVUQUhsT0VjKzl0NnJrCi0+IHNzaC1lZDI1NTE5IDROTEtydyAvc1ZTc0hRd3R4
V2U4dEpaeEZEZlVyc2ZHSlNiVSt3T1M3bndsVXpPNkhVCmt2WlplcVcza2YxV05T
bnRIOWpMYWpxck5jSzRtQVZ2L2ZQS3J1VjVTWDgKLT4gc3NoLWVkMjU1MTkgYzRO
UWxBIHZCKzR5RXN2RmYvc3ZPTGJRRG1peUN6bmNuVFpSd2NPdWJwTTJYdHllRW8K
TEo4QUI1a3ZzQ3NSQldSN1lTMlVwVGhTcTRPaHpYVHZVdHowYmZqR1czVQotPiBz
c2gtcnNhIERRbEU3dwpvRzJjS0h3b2p4RXJrSDI1VEszQ1BvckNUS2laV3hGVVlx
TXd0VkcvQ1hvWElNWnVNa1JoeHhMTDFUUWZnV0YxCjNHajRtZ0lmQzVXZmhyM1Nq
SWoyVTFFdmowNTdZV0J0WHE5cXdZb2U1OVJOb1hYb1lnSjdqN09Bd2IvOUFlckMK
d3hBc3pUK1J2Y3VnTjNSR0Q0cVJUVlluNllBazJ2L1dmOXVvZXI0WjVYbjI1RXhr
Rm1EMzkxcStrYWRJTFNJKwo3YjZ6MEZ3RmtLZytiWGdBWWZEYU9EQWxEZWtzV3dD
dGhJUUd0ajcvUFlxVGJqTVhlWmhSeHpkMHQyWWs5ZS9DClNEV0gzcWM2YlJTQ1lM
bE4zTFJKSFU1VjAybFJvY1FQR2tNSFo2NFVDczBTUW9UWjY4aDVNUzlYaVlkdzJm
dUsKckVaVld4YXpsdGYwWHVrWEtTbDJBU2cwcXlJSGY4ZEhlT2o4QlRBVGxWRHNL
OXlMTGp5b1ZWbnJYV2pzaFFIcApQVmJzVi9kWnRpeWcyaS9weVN3SncxQjhlanBV
dW5FU0VtL2lpS0NSTWtoOVI4akxXOUlhWW5xMkp2SUszSU9QCk1rZVdoNkI5TGRB
L2twWWpPYVlSWUxrRElRcjhHMFczM29lcWlWWlJhbk0vcGFEQmw3UGdXWXJBNXJV
VURYcFQKCi0+IHNzaC1lZDI1NTE5IENTTXloZyBpOEE3YUFzcytOYndXWWNreThp
ZEdveGFlbHlPa0FwZ2xBY0lCcmIycTM4CnI0ZE1DM2d1bzZPV3diL2lZQi9SUnB1
UWFjVVFueTFpMzRINm9Ob0pZMTQKLS0tIFJwZy9uRklOUEl0Z2hDNmx1YWsyNVov
aHZ5VVhtMnVyYTMwbCtkaFFtR28KZzkT96InPG4YYyVKq0ZOrIBPtCJBbTXUJu+8
9G03diIRwzYb6cSBRMtKKGl5NEfbJE7B2OnXeHPeCIPiGArKudKxFg9COHGOUP0h
hUDDL9RGVhd4sMs8zRkxNghRwQzD
-----END AGE ENCRYPTED FILE-----
+30
View File
@@ -0,0 +1,30 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVrd2NzQSBKZllV
QytCeGYyVmtRMUxudm5iWmF3aWdITlRQVE53RnhFNUFabXkwbkg0CkE5Wk1mbXdm
TzRuQTdvNklORUdSdHFIZm1zSVdvMWVnWWpRTUxDSzFBMHcKLT4gc3NoLWVkMjU1
MTkgM0JjcjF3IDlISlRMdC9Sc2hic3JRV1o0WDNsbzByNmdFNnZDdjJSLytEa0xS
VzRzQ2sKYXJGSjJDa2xuYThLNnhIMFhwTTNUSHJWbnFFYzZSRFQxVlVWK2xjNWgv
dwotPiBzc2gtZWQyNTUxOSA5ZDRZSVEgaGUweXhJZmhTOGt3VFQ2R04rQjNOckxx
c3BCMTBWZ0lsQmU3NmlVRkRYWQp6QlMvQkZmemVjekdLVWlDTUlNRG04MTlzaHAz
cW9xd2JjNlE0eFF4NzNvCi0+IHNzaC1lZDI1NTE5IDROTEtydyBWQ2FGUCtFQ2FZ
Wk5uSzZyTDNBTHRPUTkvTlRWR0Q1bHdqRjBycmU0VFc0CkdpSUNZZGw1aGJwUVk0
VnlIR2IxVTJNaFVSNHJQSDR2YWxuN2ZiSERQSUEKLT4gc3NoLWVkMjU1MTkgYzRO
UWxBIHlDYUlJRGxabTZxSnNpTUNWUDRXWUtHa0h3Z3BYTlZkUEtkall1OUh1VzQK
ZEQzSW5tM0xFdWd1a3hibXlxa0N2N2RlemUyMkplRW5CNFZHUCtTSEhWdwotPiBz
c2gtcnNhIERRbEU3dwpoNE9FdlpHYXJFZVRYQnBZNFZPTUhLb3RDdWpNcWxtU2gv
MVF5Tm1tdHBuK3NLQjBnM2JudFFwQVZnb0VpUUljCjhKQk5HQnVrY05yeU53QWhK
MG9QUVN5M2NXUEw2eEhQajFmaHZ4cWpFaXcwd3ZrSzNRWFNRREl5bERac1Y2LzYK
c3BDVXlvQ20xaUt4KzZVRkhiNU5TYkpsbGQraFdvM3RvSkFZRU54SUV4ZWNnVG0y
VVR6Q2swV2RFeUZwNGZxdQoveVhYN3JXVXBpQzdxa1ZKVXROcXRDQXladml5c2ZD
ZjFVSVRaUkZiRVlNUHA3bDg2ek9mcXJnVG9YYStza3BFCktzVXlyZHNCZ1BxYTBO
TUt1T3ZYQmR2VjErdVpPWVBOVDlFZm14Ukp0dkdUamNxODZPNFNTMXNabzNWUzVT
MHIKUkJocFBhNitCMzFkWUhyV29FU3RMWjkvMWNxbzJFbTBHb2NpbW9Cb1dheHhn
Y0N6S2FIQ2NROWtqQm5ORWxtYwo1NnF2RVQybnY2Wm5CQlJZOXVTVVNLUGZIbWZh
V2laZWFQaXhEaGdma3pIWnlSaFpjSlRqNHlRZGNiaWhsWXlYCi9UM0Z2MldUMW01
ekZja1hLNXptaFVCVFN5UVVlRGh0L2wvekxEckNmNTJOTUdoUE9wRzFaNzhqQnpM
dThmWDUKCi0+IHNzaC1lZDI1NTE5IENTTXloZyB5ZzV4Q1pIOGpBOEtUcG1rNVZB
QjFPbEt0OThxNWl3V1VhRTV2RFVrUGhRCmpwSHpUTnhNdlRHMnRvYWk2emJlSlhJ
aWwweUg5dG1rYmRMMEVDTVdHdnMKLS0tIHp6alVpN1IvQ2E5UEV0Zm1nQk53cWJQ
WCtTN08wSXlCSktkaDlPRG9Wa0UKyLX9C4xDpcPIVFsimn4OmCWAKZ1IPxeSzgr0
W6Shg1EWCeMm3dQVJ8O9mji4JW/SJHKwqlJvFTMPhIwcdIo=
-----END AGE ENCRYPTED FILE-----
+31
View File
@@ -0,0 +1,31 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVrd2NzQSBTNk5O
dmZTRnRnOU5JMHU4QitlWWMyaXhscm1aQWNQSWJmVjU3MHdvVmowClkrVTV6VVVV
Q0tiWXlkcGtHS3AwTUtqYmJwdnowMXFLcGNYb1dVZmpVTGcKLT4gc3NoLWVkMjU1
MTkgM0JjcjF3IGVpdFlFTUFZQS9IMzVaRkNHSHltcHV1QlFmYk1yRitvZjhZZzI2
ZDlTR3MKQ0RKT0tWZ0JGOUZCMTk2bitENmxnZzc1VDloNktvaVJFdGxyaVFaMjhJ
VQotPiBzc2gtZWQyNTUxOSA5ZDRZSVEgdmw1MTg1R3lLR1NFNGUzQXpnUS9TNjRq
czVtYlFzUmJnbmJ1c3JFRVZCQQoyYzBmQzdPbFVyU3diWlNuc1JoaHptOTFCUFBX
RFpDZk4rWHByOGp2bkJFCi0+IHNzaC1lZDI1NTE5IDROTEtydyBIZWdsejNPVkdZ
dkN3Z1FLTkNCVTdKSHNIOGU4UUUwdklhS0oxMlc5ekVjCnNZL0ZsV1VMU1p0eEFp
YzBOdndIRzB3KzZXNGRFaHBpVDQ4Wm1jb2E1Z1kKLT4gc3NoLWVkMjU1MTkgYzRO
UWxBIGFteVpubkxhcm80VEllY1llYkx4YXRTRHU4clNwK1F1SWlMTWxtMTYvRncK
YjhvRXBod2I5dUZaWGlyOHcwa3MvWjNPOCtKendiMk9zVE5mVlpTS1RVMAotPiBz
c2gtcnNhIERRbEU3dwpoenRXZkF2ck1LWDhxK25ldG1HVmRzVEh4TVNuaTdwQmVW
eW80TE5kYzd5YkFpZXZhdWVSdEh4VTA2TzhnMzNsCkZud1AvTUdWSHJtOWtEZzlj
ai9SdG1PRUtCN3VWQXlyamVpV1dWTEZkaFZpZHIxQ0c0eHIva1dzeDN6MlJla24K
dXcrNUxUWVdMTFpaRk1YbkszazNDdFhuSVdxbE9rVHNNWDBvbDF4WGlWT0d5RTJR
WmxCSFAzbDVVNWJneWhJZApPVnozRkRsS2J2VkFRVEpDVWJicmhhWXA0eVEvZFEz
eWtsY3ZaNEk1MzlDYVJDb09lZGRsVk9YMXhyOWJsNmJYCk5HRTRjb0RrdlRydjNs
T3prdXMvbnF0Y0FlL0VUbHNuVzZPNWJSK09TdEpuVU83dytiUDJRbmpFazludkkz
d3AKUUp6TkVWSWRHMkozR0lQR1JlNmZ5Nk05WkUwbWJaODUzNmJIVkNEazVRcFFO
ZnVZengyeFhQSlFsbUlta0tPRwo5OURlRTlPNERLWlF6aE9QaS91T0kyb2tIS0kz
L0FYNjhWVWp1Q2xqUVRialcyWkhXbTJwU0R5VmVkbE1GeGEyCmROTFErS2VsMEo1
VHdta2RObmtwMWtJTzRuaEhRbTFFbEE0V1RKbUk3SCtLWlE1cHR5c09ncThxbVY1
cnRETkUKCi0+IHNzaC1lZDI1NTE5IENTTXloZyBZbW1NSStwWGd5RE1kRkk5aTZX
alZtUWk5M3pnU1ZTSFU5UExnS0d4NkcwCjAvblBmUG5MUVVTOEpncjJjY0I1QzN1
eVVNMlVZenJ6MzVDRXFaMVgxREUKLS0tIFIwd1ZtanJuU0Q2Ym9kN3lSS1NtQlky
NC93UWJXa0tXTUVON3NmNVpUR0kKX71fenkAzKU3aIHFjLTpemNxsc5unQTy9f1O
jpfhFHRPG5HuUBtmi6Fuv2n8J8Gw70D0XKs6UgAYV5GY0Db1daJZRbgF9EExbadB
JQm3DLy8LG6KAM250ooGHKJoJSfQ
-----END AGE ENCRYPTED FILE-----