diff --git a/.sops.yaml b/.sops.yaml index d713073..637aff9 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -13,6 +13,7 @@ keys: - &jupiter age1zak7glavmg4026p2389fyqe769vqm4jrryknuqckgqq4merz5f7q44rkkt - &neptun age1hp72xyx2cnd05937e4eww95g5kdtn0wsf9j2nypw330pa69gfdxqn0lpkp - &terra age1rfcmu6zh40v4260l9hnf8ajs9vly0s06rx3ey76eu78dp9t7getqyhmkut + - &mars age1eapjg6tdrr0fuvmgs3q3nlvnjkaxez298qynqqqxt0lpcv0lrsyq7ayxjk # mercury (rpi) uses a dedicated age key (SD image, no ssh-host-key delivery); # the private key is dropped on its boot partition after flashing. - &mercury age1cpty7zrgnn6l97upq00w5wa8zcvnkxkdt2jvhlj97jh83exure4slha43t @@ -28,6 +29,9 @@ creation_rules: - path_regex: secrets/terra\.yaml$ key_groups: - age: [ *admin, *terra ] + - path_regex: secrets/mars\.yaml$ + key_groups: + - age: [ *admin, *mars ] - path_regex: secrets/mercury\.yaml$ key_groups: - age: [ *admin, *mercury ] diff --git a/README.md b/README.md index 8718fe1..59df4ae 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ Flake-based NixOS config. Hosts: `jupiter` (ZimaBlade, NAS + services), `neptun` (netcup VPS: public reverse proxy, Authentik, headscale), -`mercury` (Raspberry Pi 3B+, DNS/DHCP), `terra` (desktop). +`mercury` (Raspberry Pi 3B+, DNS/DHCP), `terra` (desktop), `mars` (on-site, +single-purpose: Hermes Agent only). ## Structure @@ -26,6 +27,9 @@ hosts/ vm.nix # VirtualBox test image (jupiter-vbox) neptun/ # netcup public reverse proxy + tailnet node configuration.nix disk-config.nix hardware-configuration.nix secrets.nix + mars/ # on-site, single-purpose: Hermes Agent only + configuration.nix disk-config.nix hardware-configuration.nix secrets.nix + hermes-agent.nix # Hermes Agent (moved here from jupiter) secrets/ # age-encrypted sops files, one per host scripts/ # deploy, edit_secrets ``` @@ -84,6 +88,42 @@ an installer, partitions via disko, installs. Manual alternative (USB ISO): boot installer, `disko` the disk, then `nixos-install --flake .#jupiter`. +## First install on mars + +mars is an older x86_64 box (unknown provenance, "got from work"), on-site, +running Hermes Agent only (see `hosts/mars/hermes-agent.nix` — moved there +from jupiter). Its age recipient, host key +(`~/.config/homelab/mars/ssh_host_ed25519_key`), and `secrets/mars.yaml` are +already set up, with `darman_password`/`samba_password`/`opencode_go_api_key`/ +`telegram_bot_token`/`hermes_dashboard_oidc_client_secret` carried over from +jupiter's old instance. Two things are still placeholders and MUST be filled +in before installing: + +1. **OS disk id** in `hosts/mars/disk-config.nix` (`ls -l /dev/disk/by-id` + once you have console/installer access on the box) — same `REPLACE-ME` in + `hosts/mars/configuration.nix`'s comment refers to the same disk, but only + `disk-config.nix`'s `device` actually needs editing (grub's own device list + comes from disko, see that file's comment). +2. **`tailscale_authkey`** in `secrets/mars.yaml` — generate a fresh one + (see "Bootstrap the tailnet" under neptun below) rather than reusing an + old key; reusable pre-auth keys still expire. + +Boot mode is assumed **legacy BIOS** (grub, not systemd-boot) — unconfirmed; +check `[ -d /sys/firmware/efi ]` once you're at the machine and see +`hosts/mars/disk-config.nix`'s header comment if it turns out to be UEFI. + +Otherwise the flow is identical to the ZimaBlade steps above: +``` +nix run github:nix-community/nixos-anywhere -- \ + --flake .#mars \ + --extra-files /tmp/extra \ + --generate-hardware-config nixos-generate-config ./hosts/mars/hardware-configuration.nix \ + --target-host root@ +``` +(stage the host key into `/tmp/extra/etc/ssh/` first, same as step 4 there). +Manual alternative (USB ISO): boot installer, `disko` the disk, then +`nixos-install --flake .#mars`. + ## First install on terra — no-USB findiso reinstall (replacing CachyOS) terra is a Ryzen 9 5900X / Radeon RX 6800 XT desktop, currently running @@ -294,6 +334,20 @@ another way in. sudo tailscale logout && sudo systemctl restart tailscaled-autoconnect ``` +### mars + +- **Confirm the OIDC redirect still resolves.** hermes-agent.nix reuses + jupiter's old Authentik application (slug `hermes`, redirect + `https://hermes.mgaction.town/auth/callback`) unchanged — nothing to + reconfigure in Authentik, just verify `neptun`'s `hermes.mgaction.town` + vhost (now pointed at `mars.orbit.sol:9119`) actually reaches the + dashboard once mars is up and joined the tailnet. +- **Carrying forward old chat history/memories:** mars starts with a fresh + Hermes state dir (`/var/lib/hermes/.hermes`). jupiter's old instance data + is backed up at `/mnt/data/AppData/hermes.bak-2026-08-21` — rsync it over + (via the `/mnt/jupiter` samba mount) before the first switch if you want + it preserved instead of starting clean. + ### mercury (Raspberry Pi 3B+) - `./deploy flash mercury /dev/sdX` writes the dedicated age key to the root diff --git a/flake.lock b/flake.lock index 355816e..0756cc4 100644 --- a/flake.lock +++ b/flake.lock @@ -114,27 +114,6 @@ "type": "github" } }, - "flake-parts_2": { - "inputs": { - "nixpkgs-lib": [ - "hermes-agent", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1782949081, - "narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, "flake-utils": { "inputs": { "systems": [ @@ -156,29 +135,6 @@ "type": "github" } }, - "hermes-agent": { - "inputs": { - "flake-parts": "flake-parts_2", - "nixpkgs": "nixpkgs_2", - "npm-lockfile-fix": "npm-lockfile-fix", - "pyproject-build-systems": "pyproject-build-systems_2", - "pyproject-nix": "pyproject-nix_2", - "uv2nix": "uv2nix_2" - }, - "locked": { - "lastModified": 1787007062, - "narHash": "sha256-C1SJX0VqlxrLknGhFKj7+DKpfJ1eI4udPE67kW+dT+M=", - "owner": "NousResearch", - "repo": "hermes-agent", - "rev": "55e34fb7d0cd8fd1e16be82a1ceafee05fca63d3", - "type": "github" - }, - "original": { - "owner": "NousResearch", - "repo": "hermes-agent", - "type": "github" - } - }, "home-manager": { "inputs": { "nixpkgs": [ @@ -186,11 +142,11 @@ ] }, "locked": { - "lastModified": 1786924861, - "narHash": "sha256-hftabkb+73OcGzvwFAjCiQorAhprs9TnU1+FkGO5CIw=", + "lastModified": 1787146702, + "narHash": "sha256-YbRcLdU/yK4gWsQg7V8WTKZHfXL33g8+wSFUX3wyevs=", "owner": "nix-community", "repo": "home-manager", - "rev": "09ae1b85a6db412d841d60f924b23f881f0d0a38", + "rev": "173b7e8d40fdc8c296a9c99854314f17a3a1704c", "type": "github" }, "original": { @@ -242,9 +198,9 @@ "nixpkgs": [ "nixpkgs" ], - "pyproject-build-systems": "pyproject-build-systems_3", - "pyproject-nix": "pyproject-nix_3", - "uv2nix": "uv2nix_3" + "pyproject-build-systems": "pyproject-build-systems_2", + "pyproject-nix": "pyproject-nix_2", + "uv2nix": "uv2nix_2" }, "locked": { "lastModified": 1768002052, @@ -283,11 +239,11 @@ ] }, "locked": { - "lastModified": 1783265394, - "narHash": "sha256-cq4YfNFGYzp0NItZP8tEC7xUI8OSgY4fj75AU/NSaPM=", + "lastModified": 1786747096, + "narHash": "sha256-9QqhmaLVsPhKdMSBaWKjDqeGRn8G4ov4cVuZ6JFwXbo=", "owner": "numtide", "repo": "nix-vm-test", - "rev": "1a587212d2ac8b669c6c32499015f996506b6ba5", + "rev": "c8781a0ea2d8417506fff7722eae5a6316461212", "type": "github" }, "original": { @@ -314,11 +270,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1786520020, - "narHash": "sha256-9b0hAKM8UtNsm4lD3aySuXPjPpSibh6yfYEcNvr4fU0=", + "lastModified": 1787124618, + "narHash": "sha256-aKf1k2hvYgaxP9oxDPRiv9npEJLODC9eKxk7nR69lzQ=", "owner": "nix-community", "repo": "nixos-anywhere", - "rev": "4cf3b82df8422f82657ae98c3b8374a5fd74f9a1", + "rev": "ad8fa24e11eef167fd72d49fafefa3f840312d71", "type": "github" }, "original": { @@ -398,11 +354,11 @@ }, "nixpkgs-unstable": { "locked": { - "lastModified": 1786963906, - "narHash": "sha256-3tkeMWSvHPo3tYljfXbPC/TgknikU1GvdVr/DkdfvE0=", + "lastModified": 1787111413, + "narHash": "sha256-sFosWtq21eHGJRnTc/hvf4M1obRgLEUMNm/IzllkHMA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f4b6996c4e8b9ee06ce147ec344c885f51071b14", + "rev": "afe3d8ac4395617bdcdac9f188ac8717a062e014", "type": "github" }, "original": { @@ -414,27 +370,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1785318670, - "narHash": "sha256-dN6Ou5x/+23FZLEpYP3IffO+NyJFzUlGumt1uu3MMaY=", + "lastModified": 1787101114, + "narHash": "sha256-gwrPcFf/rDjHPaVflbDZ040ZDmBTRj/7+s8ZmE2SaIM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0954f7ee2f6bb3dc7d4e3d0d8bcb8fd4bde4cfc5", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { - "locked": { - "lastModified": 1786943417, - "narHash": "sha256-b4qgjdFtlz5TAZ1Hi7DFJeqX3sjaDUnrwr9OO+O1rM0=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "0dd31db7e6dbf9ce05697c4545f6fe01accec994", + "rev": "b18a4b905f8d028dc4476412e6d6891728695379", "type": "github" }, "original": { @@ -444,27 +384,6 @@ "type": "github" } }, - "npm-lockfile-fix": { - "inputs": { - "nixpkgs": [ - "hermes-agent", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1775903712, - "narHash": "sha256-2GV79U6iVH4gKAPWYrxUReB0S41ty/Y3dBLquU8AlaA=", - "owner": "jeslie0", - "repo": "npm-lockfile-fix", - "rev": "c6093acb0c0548e0f9b8b3d82918823721930fe8", - "type": "github" - }, - "original": { - "owner": "jeslie0", - "repo": "npm-lockfile-fix", - "type": "github" - } - }, "pyproject-build-systems": { "inputs": { "nixpkgs": [ @@ -495,35 +414,6 @@ } }, "pyproject-build-systems_2": { - "inputs": { - "nixpkgs": [ - "hermes-agent", - "nixpkgs" - ], - "pyproject-nix": [ - "hermes-agent", - "pyproject-nix" - ], - "uv2nix": [ - "hermes-agent", - "uv2nix" - ] - }, - "locked": { - "lastModified": 1785115949, - "narHash": "sha256-8AM37BfyGaL2v/SZyg4PupRxJ01Y4htvM+WrTjWrPpo=", - "owner": "pyproject-nix", - "repo": "build-system-pkgs", - "rev": "62c0d86027edb1c4f39a5facc09876348144f7c9", - "type": "github" - }, - "original": { - "owner": "pyproject-nix", - "repo": "build-system-pkgs", - "type": "github" - } - }, - "pyproject-build-systems_3": { "inputs": { "nixpkgs": [ "mediamanager-nix", @@ -574,27 +464,6 @@ } }, "pyproject-nix_2": { - "inputs": { - "nixpkgs": [ - "hermes-agent", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1784591072, - "narHash": "sha256-zP/WaDxrRu8GANZM61+V2LT/7ycEEdoyLWn7M6WzU7M=", - "owner": "pyproject-nix", - "repo": "pyproject.nix", - "rev": "e3b599ca2e7fcf93d4edf65d7f19bbf6491724f3", - "type": "github" - }, - "original": { - "owner": "pyproject-nix", - "repo": "pyproject.nix", - "type": "github" - } - }, - "pyproject-nix_3": { "inputs": { "nixpkgs": [ "mediamanager-nix", @@ -619,14 +488,13 @@ "inputs": { "authentik-nix": "authentik-nix", "disko": "disko", - "hermes-agent": "hermes-agent", "home-manager": "home-manager", "hypr-chrome": "hypr-chrome", "mediamanager-nix": "mediamanager-nix", "nix-flatpak": "nix-flatpak", "nixos-anywhere": "nixos-anywhere", "nixos-images": "nixos-images", - "nixpkgs": "nixpkgs_3", + "nixpkgs": "nixpkgs_2", "nixpkgs-unstable": "nixpkgs-unstable", "sops-nix": "sops-nix", "tome": "tome" @@ -691,11 +559,11 @@ ] }, "locked": { - "lastModified": 1785945821, - "narHash": "sha256-NLSyTCW4K4ofhNBllt3omPasm6QpralXH1DBZOc91Dw=", + "lastModified": 1786901030, + "narHash": "sha256-WSFCsDSE5ffgD2MqzkM2CYjeFiKhRF/dJUN8uedb6YE=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "ae7910970dddc408fe6ab1c8e4b277bb21d72dc0", + "rev": "27b3b12a8e6375f28ebe122f07d230ca5459bbfa", "type": "github" }, "original": { @@ -730,31 +598,6 @@ } }, "uv2nix_2": { - "inputs": { - "nixpkgs": [ - "hermes-agent", - "nixpkgs" - ], - "pyproject-nix": [ - "hermes-agent", - "pyproject-nix" - ] - }, - "locked": { - "lastModified": 1785277507, - "narHash": "sha256-9Tq3UDX2hD/aveW/HvkBlAmEwJTOlY5HQXJM+L5BGmE=", - "owner": "pyproject-nix", - "repo": "uv2nix", - "rev": "5a836d395cbf5fc22670eb98dd4aa4fc4d406977", - "type": "github" - }, - "original": { - "owner": "pyproject-nix", - "repo": "uv2nix", - "type": "github" - } - }, - "uv2nix_3": { "inputs": { "nixpkgs": [ "mediamanager-nix", diff --git a/flake.nix b/flake.nix index 87d28d5..f9b2bf6 100644 --- a/flake.nix +++ b/flake.nix @@ -33,11 +33,6 @@ }; authentik-nix.url = "github:nix-community/authentik-nix"; nix-flatpak.url = "github:gmodena/nix-flatpak"; - # Hermes Agent (see hosts/jupiter/hermes-agent.nix) — Tier 2 platform - # per its own docs (best-effort Nix support, can break on any upstream - # commit), so pinned like everything else via flake.lock rather than - # followed loosely. - hermes-agent.url = "github:NousResearch/hermes-agent"; # Own Hyprland plugin (border + title bar), public repo, fetched over # https (no credentials needed, unlike tome below). `nixpkgs.follows` is # what makes its packaged build ABI-correct — Hyprland plugins are @@ -85,7 +80,6 @@ modules = [ disko.nixosModules.disko sops-nix.nixosModules.sops - inputs.hermes-agent.nixosModules.default ./hosts/jupiter/configuration.nix ]; }; @@ -115,6 +109,18 @@ ]; }; + # mars — on-site x86_64 box, single-purpose: Hermes Agent only. + # See hosts/mars/*. + mars = nixpkgs.lib.nixosSystem { + inherit system; + specialArgs = { inherit inputs; }; + modules = [ + disko.nixosModules.disko + sops-nix.nixosModules.sops + ./hosts/mars/configuration.nix + ]; + }; + # mercury — Raspberry Pi 3B+ (aarch64), DNS/DHCP. Boots from an SD image: # nix build .#nixosConfigurations.mercury.config.system.build.sdImage # (aarch64 build — needs binfmt/qemu on this x86 host, or a remote/aarch64 @@ -177,7 +183,7 @@ nixos-images.nixosModules.kexec-installer ({ ... }: { users.users.root.openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGZpkPVhzi1zG5JI9hWyUgdyvNIQbp4ts4jw3idpMhhN erik@laptop" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILD5K6AQ0wYYHbNGzC4PyunUQsXbaD0iu1eaadLtv+Xp darman@terra" ]; }) ]; @@ -201,9 +207,10 @@ services.openssh.enable = true; services.openssh.settings.PermitRootLogin = "prohibit-password"; users.users.root.openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGZpkPVhzi1zG5JI9hWyUgdyvNIQbp4ts4jw3idpMhhN erik@laptop" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILD5K6AQ0wYYHbNGzC4PyunUQsXbaD0iu1eaadLtv+Xp darman@terra" ]; networking.hostName = "homelab-installer"; + console.keyMap = "de"; # matches common.nix's real hosts environment.systemPackages = [ pkgs.git ]; # findiso= is a SCRIPT-stage-1 feature (stage-1-init.sh) only. The diff --git a/hosts/jupiter/configuration.nix b/hosts/jupiter/configuration.nix index 8e9b547..4d0c0b8 100644 --- a/hosts/jupiter/configuration.nix +++ b/hosts/jupiter/configuration.nix @@ -6,7 +6,6 @@ ./hardware-configuration.nix ./disk-config.nix # disko: OS-disk partitions + filesystems ./secrets.nix # sops-nix: samba password, tailscale key, ... - ./hermes-agent.nix # Hermes Agent, isolated instance (see file for why) ../../common.nix # shared base: user / ssh / nix / firewall ../../services/network/samba.nix ../../services/network/avahi.nix diff --git a/hosts/jupiter/hermes-agent.nix b/hosts/jupiter/hermes-agent.nix deleted file mode 100644 index 057cbcf..0000000 --- a/hosts/jupiter/hermes-agent.nix +++ /dev/null @@ -1,83 +0,0 @@ -{ config, ... }: - -# Hermes Agent — jupiter's own instance (terra no longer runs one; see -# 713d91d). Locked down given jupiter's much bigger blast radius -# (irreplaceable immich photos on an unredundant RAID0, gitea/CI tokens, the -# whole media stack): -# -# - Own dedicated "hermes" system user (module default: user/group "hermes", -# createUser = true) — NOT darman. darman is in jupiter's "docker" group -# (services/containers.nix: rootful podman with dockerCompat), which is -# root-equivalent (`docker run -v /:/host --privileged ...`). Handing an -# LLM-driven agent that identity would mean a container escape = root on -# the whole NAS. -# - container.enable = true, backend = "podman": the ENTIRE gateway process -# runs inside a container (reusing jupiter's existing rootful podman -# instead of also standing up a second Docker daemon), not just the shell -# tool. Per upstream's own SECURITY.md this is "whole-process wrapping" — -# shell, file tools, MCP subprocesses, and the code-exec tool are all -# confined, unlike the lighter "terminal-backend"-only isolation. -# - Its own Telegram bot (own token, in secrets.nix) with an EXPLICIT -# TELEGRAM_ALLOWED_USERS rather than relying solely on the adapter's -# fail-closed default. Sharing terra's bot token would 409-conflict two -# long-pollers on the same token. -# - No container.extraVolumes into /mnt/data or the homelab repo — nothing -# valuable is in reach if a command goes wrong or gets injected via -# Telegram/tool output. stateDir/workingDirectory live on the array -# (below) purely because coding-task state (repo clones, npm/pip caches -# inside the container's writable layer) belongs off the 29G eMMC, same -# reasoning as postgres/containers.storage in configuration.nix — NOT -# because anything else on /mnt/data is exposed to the agent. -{ - services.hermes-agent = { - enable = true; - addToSystemPackages = true; # `hermes` on darman's PATH for interactive - # debugging over ssh — routes through to the - # container, does not grant darman any group. - - # Off the eMMC: stateDir bind-mounts into the container as /data, so this - # is where any future scoped repo clone (container.extraVolumes) and the - # container's own writable layer (npm/pip installs during coding tasks) - # actually land. RequiresMountsFor below (mirrors podman/sabnzbd/gitea-runner - # in configuration.nix) keeps the service from starting — and bind-mounting - # the wrong, empty eMMC path — before the nofail array is up. - stateDir = "/mnt/data/AppData/hermes"; - workingDirectory = "/mnt/data/AppData/hermes/workspaces"; - - # HERMES_TIMEZONE is the highest-priority source hermes_time.py checks - # (ahead of config.yaml's `timezone` key) — the container has no host - # /etc/localtime bind-mount, so it defaults to UTC otherwise. Not a - # secret, so `environment` (plain .env) rather than sops. - environment.HERMES_TIMEZONE = "Europe/Berlin"; - - container = { - enable = true; - backend = "podman"; # jupiter already runs podman (services/containers.nix); - # default "docker" would stand up a second daemon. - }; - - # OpenCode Go provider account (same key originally used for terra's now- - # removed instance, copied into secrets/jupiter.yaml — just an API key, - # not a stateful identity like the Telegram bot token). - settings.model = { - provider = "opencode-go"; - base_url = "https://opencode.ai/zen/go/v1"; - default = "gpt-5.6-luna"; - api_mode = "codex_responses"; - }; - - settings.platforms.telegram = { - enabled = true; - home_channel = { - platform = "telegram"; - chat_id = "15151223"; - name = "Erik Simon"; - user_id = "15151223"; - }; - }; - - environmentFiles = [ config.sops.templates."hermes-agent.env".path ]; - }; - - systemd.services.hermes-agent.unitConfig.RequiresMountsFor = [ "/mnt/data" ]; -} diff --git a/hosts/jupiter/secrets.nix b/hosts/jupiter/secrets.nix index f98e941..e79f12b 100644 --- a/hosts/jupiter/secrets.nix +++ b/hosts/jupiter/secrets.nix @@ -64,21 +64,4 @@ sops.secrets.sabnzbd_eweka_username.owner = "sabnzbd"; sops.secrets.sabnzbd_eweka_password.owner = "sabnzbd"; - # Hermes Agent (hosts/jupiter/hermes-agent.nix) — a separate, isolated - # instance from terra's, with its OWN Telegram bot token (sharing terra's - # would 409-conflict two long-pollers on one token). opencode_go_api_key - # is the same provider account as terra (hosts/terra/secrets.nix) — a - # stateless API key, fine to duplicate across hosts. No owner override: - # sops.templates renders via a root-run activation script, which the - # hermes module's own activation script (also root) then reads — unlike - # sabnzbd's preStart, this doesn't run as the service's own user. - sops.secrets.opencode_go_api_key = { }; - sops.secrets.telegram_bot_token = { }; - sops.templates."hermes-agent.env".content = '' - OPENCODE_GO_API_KEY=${config.sops.placeholder.opencode_go_api_key} - TELEGRAM_BOT_TOKEN=${config.sops.placeholder.telegram_bot_token} - TELEGRAM_HOME_CHANNEL=15151223 - TELEGRAM_ALLOWED_USERS=15151223 - ''; - } diff --git a/hosts/mars/configuration.nix b/hosts/mars/configuration.nix new file mode 100644 index 0000000..9319f1d --- /dev/null +++ b/hosts/mars/configuration.nix @@ -0,0 +1,49 @@ +{ config, pkgs, ... }: + +# mars — on-site x86_64 box, single-purpose: runs Hermes Agent only. +# See hermes-agent.nix for what that is and why it moved here from jupiter. +{ + imports = [ + ./hardware-configuration.nix + ./disk-config.nix # disko: OS-disk partitions + filesystems + ./secrets.nix # sops-nix: samba/tailscale/hermes secrets + ./hermes-agent.nix + ../../common.nix # shared base: user / ssh / nix / firewall + ../../services/containers.nix + ../../services/vpn/tailscale.nix + ]; + + networking.hostName = "mars"; + networking.networkmanager.enable = true; # DHCP on-site, same as jupiter + users.users.darman.extraGroups = [ "docker" ]; # merges with common.nix; podman debug access + + # ---- Boot (UEFI, confirmed) ---- + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + # jupiter's samba share (services/network/samba.nix) — mounted on demand so + # mars doesn't stall boot/login when jupiter is off or unreachable. This is + # also where Hermes's shared dropbox lives now (hermes-agent.nix). Modes are + # tighter than terra's equivalent mount (0770 not 0755, gid=hermes not + # gid=users) since the hermes-agent container (uid 986, gid 983 — no podman + # userns remapping, see services/network/pihole.nix) needs group write into + # it, not just darman. + fileSystems."/mnt/jupiter" = { + device = "//jupiter/data"; + fsType = "cifs"; + options = [ + "credentials=${config.sops.templates."jupiter-smb.credentials".path}" + "uid=1000" + "gid=983" + "file_mode=0770" + "dir_mode=0770" + "nofail" + "x-systemd.automount" + "x-systemd.idle-timeout=60" + "x-systemd.mount-timeout=10s" + "_netdev" + ]; + }; + + system.stateVersion = "26.05"; # set at install time; do NOT bump on upgrades +} diff --git a/hosts/mars/disk-config.nix b/hosts/mars/disk-config.nix new file mode 100644 index 0000000..0364689 --- /dev/null +++ b/hosts/mars/disk-config.nix @@ -0,0 +1,37 @@ +{ ... }: + +# Declarative OS-disk layout (disko). UEFI: GPT with an ESP + ext4 root, +# same pattern as jupiter/terra (confirmed UEFI-capable, not the legacy-BIOS +# guess this scaffold started with). +# +# ⚠️ This disk is WIPED on install. Set `device` below to the real OS disk +# ONLY (by-id) — `ls -l /dev/disk/by-id` once you have console access. +{ + disko.devices.disk.os = { + type = "disk"; + device = "/dev/disk/by-id/ata-Samsung_SSD_840_EVO_120GB_S1D5NSAFB10834Z"; + content = { + type = "gpt"; + partitions = { + ESP = { + size = "512M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "umask=0077" ]; + }; + }; + root = { + size = "100%"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + }; + }; + }; + }; + }; +} diff --git a/hosts/mars/hardware-configuration.nix b/hosts/mars/hardware-configuration.nix new file mode 100644 index 0000000..8f44d45 --- /dev/null +++ b/hosts/mars/hardware-configuration.nix @@ -0,0 +1,18 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/mars/hermes-agent.nix b/hosts/mars/hermes-agent.nix new file mode 100644 index 0000000..23a70f4 --- /dev/null +++ b/hosts/mars/hermes-agent.nix @@ -0,0 +1,146 @@ +{ config, ... }: + +# Hermes Agent — moved here from jupiter (hosts/jupiter/hermes-agent.nix, +# see its git history / b5fa599 / 713d91d for the terra->jupiter->mars +# lineage). mars is dedicated to this one service, on-site, with no big +# data array of its own — unlike jupiter it has nothing under /mnt/data, so +# state lives on the local OS disk and the shared dropbox rides jupiter's +# samba share as a CIFS client instead of being served locally. +# +# Runs the OFFICIAL published image (docker.io/nousresearch/hermes-agent — +# real and actively maintained, contrary to what the checked-out repo's own +# README/docker-compose.yml suggested; verified directly on Docker Hub) as a +# plain podman container. It never sets HERMES_MANAGED or writes .managed, so +# Hermes fully self-manages config.yaml, profiles, memories and skills at +# runtime — no redeploy needed except to bump the pinned digest below. +# +# Security posture: +# - Only two paths reachable: its own local state dir, and the small +# shared "dropbox" below (via the jupiter samba mount) for darman to +# hand files to Hermes — nothing else on jupiter's array is reachable +# if a command goes wrong or gets injected via Telegram/tool output. +# - Its own Telegram bot (own token, in secrets.nix) with an EXPLICIT +# TELEGRAM_ALLOWED_USERS. +# - Runs as a rootful podman container (services/containers.nix) with its +# OWN numeric uid/gid — not darman, who is in the "hermes" group for +# host-level debugging only (`podman exec -it hermes-agent hermes ...`). +# +# Dashboard (HERMES_DASHBOARD=1) is gated behind Authentik, same setup as on +# jupiter. Its default bind (0.0.0.0:9119) fails closed without an auth +# provider registered, and 0.0.0.0 (not loopback) is required so neptun's +# Caddy can reach it over tailscale0 — reachability itself stays LAN-closed +# (no networking.firewall.allowedTCPPorts entry; tailscale0 is already a +# trustedInterface, services/vpn/tailscale.nix). Public route: neptun's +# hermes.mgaction.town vhost (hosts/neptun/configuration.nix) proxies to this +# over the tailnet. mars runs no Caddy of its own (single-purpose box), so +# there is no LAN vhost — reach the dashboard directly via mars's tailnet +# name (mars.orbit.sol:9119) or LAN IP:9119 for local debugging. +# +# Uses upstream's generic self-hosted OIDC plugin, same Authentik +# application as before (slug `hermes`) — the client ID/secret didn't need +# to change since the public redirect URI (hermes.mgaction.town) didn't. +# +# Data migration: this starts with a FRESH state dir. jupiter's instance was +# itself reset to fresh on 2026-08-21 (see its old hermes-agent.nix), so +# there was nothing irreplaceable to carry forward; if that turns out to be +# wrong, jupiter's old data is backed up at +# /mnt/data/AppData/hermes.bak-2026-08-21 and can be rsynced into +# ${hermesHome} below before the first switch on mars. +let + stateDir = "/var/lib/hermes"; + hermesHome = "${stateDir}/.hermes"; + # Shared drop-in folder: darman can put files here from any host. Lives on + # jupiter's array (reachable at /mnt/jupiter, the samba mount below) rather + # than locally, so it's the same physical location it always was — only + # the container reading it moved. Mounted under /opt/data so it falls + # inside Hermes's own sealed write-safe root (HERMES_WRITE_SAFE_ROOT= + # /opt/data) rather than a path its own tooling would treat as untrusted. + dropboxDir = "/mnt/jupiter/AppData/hermes-dropbox"; + + # Pinned by digest (captured 2026-08-21 via `podman image inspect + # docker.io/nousresearch/hermes-agent:latest --format '{{.Digest}}'` on + # jupiter) rather than floating `:latest`, so a redeploy is reproducible — + # bumping Hermes is an explicit edit here, not silent drift on next pull. + hermesImage = "docker.io/nousresearch/hermes-agent@sha256:5342e518734a08f6c66b89b4262434813c28a77abbc59c230c8f1637df71a259"; + + # Kept identical to jupiter's instance purely so nothing else needs to + # change if state ever gets migrated over. + hermesUid = "986"; + hermesGid = "983"; +in +{ + # Browsing convenience (ssh access to the bind-mounted local state) — does + # NOT touch the container, which keeps using HERMES_UID/GID above + # regardless of what's declared here. + users.groups.hermes.gid = 983; + users.users.darman.extraGroups = [ "hermes" ]; + + systemd.tmpfiles.rules = [ + "d ${stateDir} 0750 root hermes -" + ]; + + # podman requires the bind-mount source to already exist (no auto-create), + # and the dropbox lives on the CIFS mount below — mkdir there works fine + # over cifs, no server-side (jupiter) config needed. + systemd.services.hermes-agent-prepare-dirs = { + description = "Create Hermes state dirs before the container starts"; + before = [ "podman-hermes-agent.service" ]; + wantedBy = [ "podman-hermes-agent.service" ]; + unitConfig.RequiresMountsFor = [ "/mnt/jupiter" ]; + serviceConfig.Type = "oneshot"; + script = '' + mkdir -p ${hermesHome} + mkdir -p ${dropboxDir} + ''; + }; + + virtualisation.oci-containers.containers.hermes-agent = { + image = hermesImage; + autoStart = true; + # Host networking: Hermes only long-polls Telegram outbound, no inbound + # ports to publish (same reasoning as clonarr on jupiter). + extraOptions = [ "--network=host" ]; + # Upstream's own documented single-mount pattern (docker/docker-compose.yml): + # ~/.hermes:/opt/data. + volumes = [ + "${hermesHome}:/opt/data" + "${dropboxDir}:/opt/data/dropbox" + ]; + environment = { + HERMES_UID = hermesUid; + HERMES_GID = hermesGid; + TZ = "Europe/Berlin"; + # HERMES_TIMEZONE is the highest-priority source hermes_time.py checks + # (ahead of config.yaml's `timezone` key) — the container has no host + # /etc/localtime bind-mount, so it defaults to UTC otherwise (fixed in + # 9403122 on jupiter; carried forward here). + HERMES_TIMEZONE = "Europe/Berlin"; + + # Dashboard + Authentik OIDC gate — see the file-level comment above. + HERMES_DASHBOARD = "1"; + HERMES_DASHBOARD_HOST = "0.0.0.0"; # must be tailscale0-reachable, not just loopback + HERMES_DASHBOARD_OIDC_ISSUER = "https://auth.mgaction.town/application/o/hermes/"; + HERMES_DASHBOARD_OIDC_CLIENT_ID = "4BqdJu3htnMtSZnyEu5zHnsSOvlEbw3Ie3mYVlh6"; + # uvicorn's proxy_headers=True (web_server.py) only trusts + # X-Forwarded-Proto from forwarded_allow_ips, which defaults to + # 127.0.0.1 — neptun's Caddy reaches this over the tailnet (a real + # routed IP), so without this the dashboard sees the raw scheme (http) + # and builds an http:// redirect_uri that Authentik rejects against its + # registered https:// one. Safe to trust any peer here: 9119 is already + # scoped to loopback + tailscale0 only (no LAN firewall rule), so + # nothing untrusted can reach this process to begin with. + FORWARDED_ALLOW_IPS = "*"; + }; + environmentFiles = [ config.sops.templates."hermes-agent.env".path ]; + cmd = [ "gateway" "run" ]; + }; + + systemd.services.podman-hermes-agent = { + after = [ + "hermes-agent-prepare-dirs.service" + "systemd-tmpfiles-setup.service" + ]; + requires = [ "hermes-agent-prepare-dirs.service" ]; + unitConfig.RequiresMountsFor = [ "/mnt/jupiter" ]; + }; +} diff --git a/hosts/mars/secrets.nix b/hosts/mars/secrets.nix new file mode 100644 index 0000000..470afd7 --- /dev/null +++ b/hosts/mars/secrets.nix @@ -0,0 +1,38 @@ +{ config, ... }: + +# sops-nix wiring for mars. Encrypted values in ../../secrets/mars.yaml, +# decrypted with mars's own SSH host key (recipient in ../../.sops.yaml). +# The host key is pre-generated on the laptop and shipped at install +# (nixos-anywhere --extra-files -> /etc/ssh/ssh_host_ed25519_key). +{ + sops.defaultSopsFile = ../../secrets/mars.yaml; + sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + + sops.secrets.darman_password.neededForUsers = true; + users.users.darman.hashedPasswordFile = config.sops.secrets.darman_password.path; + + sops.secrets.tailscale_authkey = { }; + + # Credentials file for the //jupiter/data cifs mount (see configuration.nix). + # Same value as jupiter's own samba_password (services/network/samba.nix) — + # mars authenticates as the same smb user, mirroring terra's setup. + sops.secrets.samba_password = { }; + sops.templates."jupiter-smb.credentials".content = '' + username=darman + password=${config.sops.placeholder.samba_password} + ''; + + # Hermes Agent (hermes-agent.nix) — moved here from jupiter (see that + # host's git history); same Telegram bot token, opencode key, and + # Authentik OIDC client secret, so no new bot/app to provision. + sops.secrets.opencode_go_api_key = { }; + sops.secrets.telegram_bot_token = { }; + sops.secrets.hermes_dashboard_oidc_client_secret = { }; + sops.templates."hermes-agent.env".content = '' + OPENCODE_GO_API_KEY=${config.sops.placeholder.opencode_go_api_key} + TELEGRAM_BOT_TOKEN=${config.sops.placeholder.telegram_bot_token} + TELEGRAM_HOME_CHANNEL=15151223 + TELEGRAM_ALLOWED_USERS=15151223 + HERMES_DASHBOARD_OIDC_CLIENT_SECRET=${config.sops.placeholder.hermes_dashboard_oidc_client_secret} + ''; +} diff --git a/hosts/neptun/configuration.nix b/hosts/neptun/configuration.nix index ec06d6b..c15d6f4 100644 --- a/hosts/neptun/configuration.nix +++ b/hosts/neptun/configuration.nix @@ -110,6 +110,13 @@ reverse_proxy http://jupiter.orbit.sol:2283 ''; + # ---- Hermes dashboard ---- + # Authentik-gated (hosts/mars/hermes-agent.nix has the OIDC config and the + # "create the Authentik app" instructions — moved here from jupiter). + services.caddy.virtualHosts."hermes.mgaction.town".extraConfig = '' + reverse_proxy http://mars.orbit.sol:9119 + ''; + # ---- Gitea WebUI ---- # Gitea's web UI and HTTPS clones (services/dev/gitea.nix, HTTP_PORT 3000). # Its SSH side is the separate :2222 forward further down. diff --git a/secrets/jupiter.yaml b/secrets/jupiter.yaml index ad6299e..6dc285f 100644 --- a/secrets/jupiter.yaml +++ b/secrets/jupiter.yaml @@ -1,6 +1,6 @@ samba_password: ENC[AES256_GCM,data:K3FtKC0CrOLMyfQokmxxlyUnDPo=,iv:9bTE/S/i05LJYldOHuBuz3+g8JdTuq0ysFFgXOmfti8=,tag:77NIxy7qpHRqCmbOR83AfA==,type:str] darman_password: ENC[AES256_GCM,data:DOHHlM4Qdw4WgkN+/M51n2LMjJqq5MS0FeGEH8Pz3297yUZ8jBDOKRS/Tek9stC05JKEQRtn/vVVbAY0nXs20MwrsDMo+IEFXx7Ms90vVyYIYe5O5/0aQkPq84vcwGN0RW1Rj5Y3s38vWA==,iv:DAqHbvOBq7FT7ALbmBXJ0HadEGEsL8V2e7R99H5ZH0s=,tag:9XpiRFIbaZjMn59uHCOA1g==,type:str] -tailscale_authkey: ENC[AES256_GCM,data:lRoyOId9W0be8HFLjdq9zd5qGfvVTJMAiJrxms7SQNjiRSqCz5btcxYlwBKTE7L13KOvwCd+HX6/7VXxSFYg7RQC8r7wXaPPP9+eJLzZL4x3tt8YTxdx+A==,iv:rLJnoRT/HU3VOrOG5RXnFF2D/Lo9Pq6VqvZ9G6WIvRE=,tag:/iD6Vdn0JJAeEWVJThe/cg==,type:str] +tailscale_authkey: ENC[AES256_GCM,data:+6W85d14sdDNv6pcfM9nqVR1sg68EzStT66jI0T6AKGkVw1+W+fRGulvrejCIkZHTz+YBcHhD63irxGc/CiS7P6U2hsLWjrnHqkKvSc0Wxzss6S1b9Rbnw==,iv:dhDmL0T1poPTYbWXD4FHgliCzspEHYCqwuyxC0uOXro=,tag:QX8uedKt6fX0xVVIKiRAjA==,type:str] mediamanager_token_secret: ENC[AES256_GCM,data:g75vj1E6B029O076yV3DS/1z99Tq6wMhEVx+ULYDjHsplyA+vqVRvBlviC64V47IMqKd9k2eTGBJ98Ptv3UIjbr446xZinz0c7PZgHU0XOX4EUcB1ORloMFZIv1zUv8VRjUISUQnn/vRk60e7u8eXrOrgXjxfnoBKrdKZmqxhAM=,iv:2t0XBExC9RvbTomezka+99/LtJl64zNwneA7WWc4ju0=,tag:SVCYuw0n6JevnvoQOIkjPw==,type:str] sabnzbd_api_key: ENC[AES256_GCM,data:6UW1u2Ikmnq34t4H4k/4C44SJeFHRlaPjWwUjEfH1GQ=,iv:sGsd8Sd2pfUhTUDg6PlRzfVYejRbF69jmDTIa2fvY4M=,tag:3fOLgU1K1gKHxQ3J1+3oRQ==,type:str] prowlarr_api_key: ENC[AES256_GCM,data:ab1QACaagI8ACJXFUy8r9X9lgYwRo1byUmhBPSRWwKk=,iv:EcuF6EN/4mWxlXi6R1qDzv4rOw6AT+OGSNQaaBwjJHg=,tag:hixHrbQWU6QQZNMM5rNDsg==,type:str] @@ -14,8 +14,6 @@ sabnzbd_web_password: ENC[AES256_GCM,data:9Lo=,iv:H0Kz8A534RxX+7/Aue8Q87gCzSY5e/ sabnzbd_nzb_key: ENC[AES256_GCM,data:DNVenqhJ7wf5Ng0XRA1gJN95e+90e6D9NImOSHJv/Us=,iv:eqFn0stB5pqh0ls4/impD8gc/lOkORwEJzRP6m7u1XU=,tag:Zs8ogLBZEZLyMvFBqhfpIA==,type:str] sabnzbd_eweka_username: ENC[AES256_GCM,data:eLsTZoM8T8fAlGaXWlDaoQ==,iv:eawyGhN7+d6UfBIbI3y1qgq+MYBGrXP6VfAkSOK6llA=,tag:ELOfQGHU5NOxZFhKOKf8LA==,type:str] sabnzbd_eweka_password: ENC[AES256_GCM,data:Mt3ZHAe2wzacCQq3x9Uy8WxjrVNad1SmU6sl8ZgrkMLymfq2eP4JzO/uPdD33A==,iv:PnFT95Zxqz4QBpPF5PRloKpoa15AU7Ef/Owwy+iDotw=,tag:/uRX00RzHLJN3gws5Qz8SA==,type:str] -opencode_go_api_key: ENC[AES256_GCM,data:7kgWiye0wHCxzKFsrzX2WQNDkSVpuvJN6w5Zw9tuyYj5ysDRnWDjCvQtWEJlBalq+Fz7HfT28uFLFtrjFornGEPPdQ==,iv:9Ue/nMpJozVy7oHvhvHwKNuMlsb3tXjwnpC3jok5IWs=,tag:K2UqXBnMH3lpnATa2A/Agg==,type:str] -telegram_bot_token: ENC[AES256_GCM,data:wU3CgKqbO1twJMIAlVi6rzVP5IUu34l1JOBVnlvTzhGL+Teq/sodQ9nlZOkzfg==,iv:8WxWDkGitljLa8aiwiT8td/3WeEnZAvz38oVPF5TQ4I=,tag:x9ldeAJOsTUJqAE4YZMPNg==,type:str] sops: age: - enc: | @@ -36,7 +34,7 @@ sops: CzjSDQZTcseEXZNwuzZcfB5Mvq0BQvjOj7lGuxzuE4qwWkdJWGfVLQ== -----END AGE ENCRYPTED FILE----- recipient: age1zak7glavmg4026p2389fyqe769vqm4jrryknuqckgqq4merz5f7q44rkkt - lastmodified: "2026-08-19T19:44:22Z" - mac: ENC[AES256_GCM,data:ZgHrvBYUeUDo7ZydN3K5CbIUXDvbvj1whnSWuzc+x5TlejqQH89zXRMKBuDD2DgzS+ET6PFHgN+0KoQchOI9SVGmdzz0b3mKpAMZc1BXYWOy5OcxF2xToA2Gub+QV3KQ1VsLSuR6bHcPKR6h4vAs1iwRKYAguQqo7LJMKAduuWs=,iv:EhUYS1iZOT9AbTk2A0EfLFjqIjLU7KYd1xHWwbwzUpU=,tag:Lh2yKA2AELEZk8axXdJJiA==,type:str] + lastmodified: "2026-08-21T23:14:15Z" + mac: ENC[AES256_GCM,data:7ts5oWyiPAUtF8OokDqzjZnoH0CCRwdsvF330SeCBnoyATXsWsXOvrLdTVBJf24MSMyJxCQSqBUpzKVVIlVOL1C4KjA+axr34M4oWJ/kEUReO1q9Lrl3/SuuV8PLji6/Z7pTU9tuhl4jsIPdzDsM9oZv6PbxXeex/d4fiw8Qex4=,iv:KX/xBM7HZ2NoCt4T8dhYA7o6h2eBAOdsegEplbxIAnM=,tag:KyddDKzAXV6jvMjnEV0H2Q==,type:str] unencrypted_suffix: _unencrypted version: 3.13.3 diff --git a/secrets/mars.yaml b/secrets/mars.yaml new file mode 100644 index 0000000..e73f14f --- /dev/null +++ b/secrets/mars.yaml @@ -0,0 +1,30 @@ +darman_password: ENC[AES256_GCM,data:3Kj3wfGDfS2vvTCaYC87Aq8ak2DDWqDP0YjxXk8V1CMkA2wnAP41aw++/soU0gpyMGDOY759WqRu0Wzup0Gbg4ywzQKoxbuz8Oi7mV/FxxraYsBlej0R4t+1484msrboUV4hdVdn4XdPuA==,iv:jqFcanbGXFgPNnFaZ/+TzfoUPZOJTNFcyZIGCKqC5is=,tag:bwbYiNwL79h5R30feyxpBw==,type:str] +samba_password: ENC[AES256_GCM,data:4eBsiIGLdImuf7fdCItb8GIfR5A=,iv:+ryMF+7kJrDKw7qLWpP6asZzu85pFFJuOan1NMwIbr4=,tag:s8XslqPR0ptdVaOWDWRiyQ==,type:str] +tailscale_authkey: ENC[AES256_GCM,data:An+OPDZF9kmemzoDhZPo7yMljksCz3yE/W9I1EAwtjXH3Iwe/L93Vr+WsWmw/mbvJFXMi2Vk20JIm6D/lm0zJe0qsd7Ooaj26h3xiAfG0PtoxUt+ABFXkQ==,iv:ShgYTnTb1VLOMYPJHjfs+LSebMI2fkKxU7wFzzFtiTo=,tag:9eSYCNKllY/xkhC1gC7hRw==,type:str] +opencode_go_api_key: ENC[AES256_GCM,data:x7V6iRrP6UMvMAYh/25bcrE10MHhL9lasCYRHiQ3PIDI6aL+uXP0/YpfrRPY+60m5Yv+Bd7+9aWTWdAVu1laSNjJGg==,iv:EmEAig+fSMYX+g77UpkiQ0USxUYOfFWX4WjIj9NA9N8=,tag:Pr+EZW6uDTSGjng8iG2SZw==,type:str] +telegram_bot_token: ENC[AES256_GCM,data:WX+KFtoqFodkoWNwd7EXUrUJakZ9oaMZgg4OnCeL/JVXcsdQesD1PLmKp6vK9g==,iv:m1oqKlcesvhMLtndyp/XxsUAy0YpEsSulPDK0V+Wh0A=,tag:zvLcxcQ+A4fQUht5GkL2Qw==,type:str] +hermes_dashboard_oidc_client_secret: ENC[AES256_GCM,data:IMPNTPMKO+b7eyV4hyGfnvH1/i+W4IPDNjncoyB1oIV8WaB6nOJn0sSEuTUCKB94K+Y7bsVQU0zpbKdIYOdGqgmPzwMCsScxMt4SewTmiiqWxv6SQFf4EzMxgXqjMvH8PWDzLcI2C2tI/KcVS251iqRViOTFe1/tkm+mV8sJmEI=,iv:F/rOUDmJZoGPS9fObAni5ntyOqbbhMWDPdHGLTexwlA=,tag:ALf98DmB0JziGspZMiLCiw==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5NkwwZlB3bEptRmhlUS9u + NCtma3lERDAwdDNuTlF4clhTbVlBb1pFMENJCkxuUVp4SkdCTmZQNm5lYjNzZnlq + RWZEcGVtNWM3b1R3SkZaWXI3NjVSNG8KLS0tIDRmUjcwa2hjeEFTNEl3QndQbzlp + V0dFRWJjSldOcTVoNHVGbkgwMmRTdTgKdhINgxsZ5Y8qRF1yDQUOQAwfi8NTEFvw + /+WJUFY4fuDW/2o9Cq+UMNT6YXEQQ3kyRmz/Qb/+rD8XwlM9mLGg6A== + -----END AGE ENCRYPTED FILE----- + recipient: age1cekcqyf7073fsytcjxaa9dr9zwkmn4vjg36rv2tgxdglzfv4jvxqvcj6z2 + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqTmR3T0c1RjFvR3FOQUJK + SUxwcENlQUxRTndKdURnV0p5L3A1Sk1odHcwCmx4VFc5ZWdrZ3JNc3JkMmQ1M3li + WG5ZWG9uZmliKzYzam5oMjhFV0lOTkUKLS0tIGc1MkNSVE50NTl3STBZdlIrZCsx + TGR0RmVubmYwVExKV0QvamJnYWgrWkkKAZuwoC4Q4JXKv3tNo5MaKKooUgkwZvs4 + oyJ7PS3lW+PxH5AZkeeU7gXO/pz2oDku0aDOds7kaD3n0+qSWicQ+Q== + -----END AGE ENCRYPTED FILE----- + recipient: age1eapjg6tdrr0fuvmgs3q3nlvnjkaxez298qynqqqxt0lpcv0lrsyq7ayxjk + lastmodified: "2026-08-21T23:24:48Z" + mac: ENC[AES256_GCM,data:p/vtMVfVWfgP7+xa8sbbdYLeLCYbjvthHoexmKU0RyIl2jv5bp9ldD1F1G8U+v+8+Re44DClcDtoNXrIHlreFx3hbH9sF2tnL4+oI2os7Yn+nmWZ6J1ngS2Hz+qspWtFt+up/TkP2BsTzoas5ZstJckOW0AkeKEYIT1zQmzcBtE=,iv:O2VoZK1DmRSJIR/bC538Ley1fkZqjI0L80YpjUU9mPo=,tag:xO9wzAocmxoxrIHcnBRGXQ==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.3 diff --git a/secrets/mercury.yaml b/secrets/mercury.yaml index 478587a..3de5f53 100644 --- a/secrets/mercury.yaml +++ b/secrets/mercury.yaml @@ -1,6 +1,6 @@ darman_password: ENC[AES256_GCM,data:iZQERcXtyH+91yUc3r7U6jnFYrGQPFeCPk/9ZDfxOhPLlGMX3/iEZ+SzZ7a7rDKUeUAaQUsrqANLDLclRYm4Ngo09EkbDxBx5x2GpQwlqSAS45LHnTen9LTzisWghdy79Xnilq322eaB3g==,iv:ozx/BPLR8nZTKHZroKrrh2z6ZlVCuLydQ3aNY4XvcIg=,tag:CSrq5ZipYxtXTT8RintuHQ==,type:str] pihole_webpassword: ENC[AES256_GCM,data:5iOTqD0CcbOCnM1b4+RbajMTyAU=,iv:2ZRW7dshnPzWkuudrn6n92y4Z2n/6fdnBB7BO5/ypS4=,tag:8UZSqJM6dVECQgrF6v5Fuw==,type:str] -tailscale_authkey: ENC[AES256_GCM,data:t/GuqomGLOXnGOOw2fNdbzfBMloz3WoJV+WBEvnFmxYpIBLdJwas4aJD6Jpc+SW+gsb1njb4JPxl11UMxuzBbno8DCIZv21DOc1vVbl4VcDI7kvrw3EkEg==,iv:0I/0sR/Uvbhcq2eqGPQEs1jCFhTkJrtQ/Op2Uvj2LPk=,tag:mrWn/yvy8GhvuqrScR7vrQ==,type:str] +tailscale_authkey: ENC[AES256_GCM,data:swWBS6icqidKMBC6Fo8IyOWIswWzGpRJGhFfA1JPlZsvqEo46J/kLjC6wfU4eOhSBsWTYiiqtHDaX05SKr8gwSxA/ERwj/Swf8bNHST4rbKrI4Cq5QDfzA==,iv:UUdVgkFATla6pmErn2oT06PuQ/kv9L8g0nX2CCPaJhI=,tag:97gAUSfxHzemVljl8FTULw==,type:str] sops: age: - enc: | @@ -21,7 +21,7 @@ sops: x6FfYadcRfqvSX60l6+TGdzq6xDpxLIZOJ8q19qZsAvB0in50HW5gg== -----END AGE ENCRYPTED FILE----- recipient: age1cpty7zrgnn6l97upq00w5wa8zcvnkxkdt2jvhlj97jh83exure4slha43t - lastmodified: "2026-07-24T23:31:02Z" - mac: ENC[AES256_GCM,data:I3RHwPnGHFXAeRJ4ic2plfhHMz91488KvzN6V15evutEN++affTqJIvI/Hh5Q5LAn8Q8hNkCS9l5Jl/snz+qtlwsSscckcs7jpWSI9cgSYjgEX6DjKpS+95qJo+SabzLdkiY3BlTjzcFZg7CG/lB5MunWgENY/Ad4GGlmUFnMeo=,iv:bWRltxSAd+fiIfQIp+ieqmpob6Fzbrmk1wRJDz3CjSI=,tag:r6BViobJD3MsvLw7Yyj4Rg==,type:str] + lastmodified: "2026-08-21T23:14:15Z" + mac: ENC[AES256_GCM,data:zxV+szKjxb+7EV/hSyFeHUs/V2wZgIz8NO78/RDZeGoGtCjwDGiSIFsO1VQI5LZPW+O+pTnT2s4W5P0QuEjYrkPU5LRunW+Tv87XrDBqoR62vPvhRmt0wZXOQZu4oAn7LGpn24xo5QYKc2JowJWIVlyQs03UL3jQtgwfkG9u8k4=,iv:IZpZlUVqaKjO0aokwtF2hFAqC2D9H5bVMO6HezmYQ+Y=,tag:ij6pu3b6CQzctrJ87ifp5Q==,type:str] unencrypted_suffix: _unencrypted version: 3.13.2 diff --git a/secrets/neptun.yaml b/secrets/neptun.yaml index 388c450..d5a11ff 100644 --- a/secrets/neptun.yaml +++ b/secrets/neptun.yaml @@ -2,7 +2,7 @@ darman_password: ENC[AES256_GCM,data:7G2Hgh13TxI6ugw2ebp9UtLTQ7HRC/hrCga0FmZo8h8 authentik_secret_key: ENC[AES256_GCM,data:qLrAWBywlMqT6D3FYDqE9I/5Ep+zDVu9ns9TA4UVOBSQ1uFGEj01TU9norqPf12pi9/Qs32mVzm5BqDG259DxdGT9DZVQc03QVwiIQYtmWo=,iv:OVCIxIP1Xv+nHmYsrxaPgYWQiwzVPUe8pnbyMBVAuoI=,tag:+ywkwPqkY1Dkx1R5cUJ7PA==,type:str] authentik_bootstrap_email: ENC[AES256_GCM,data:OmqpKAiiFyS/rytnHYRZ,iv:VXiPV5VfduC/IW+E3gDlNAeE+hr+IZ9W7Ty6Npuu59Y=,tag:rjvTHnBxN/pMvpQC5W9S6Q==,type:str] authentik_bootstrap_password: ENC[AES256_GCM,data:QPCY0ni3jBQY5HyK+vRlyT4YTEo=,iv:41u1Jf+WYksPUY4pvKdHQA1RHW21GAfdVDRuQ7XtdYc=,tag:SYmzv26GbE1kwleXyKLG4Q==,type:str] -tailscale_authkey: ENC[AES256_GCM,data:1uxBqERZ7U+Fr5vBaGGKajN5KsRzRpdxGBULQ6b5hSgpt2MIC3/Th4zHGNdOl5xYmH2STYv8IOOQrb5p8Xr/55laWQbOLPlfIpCNUbiVWk7uEX9+ctTSiw==,iv:Lm8vx3Oku+isW2WqSNGrkf8aqGoAwVsJQwqH3jOv06Y=,tag:opZeSMyf3bdrLnPM+yES5g==,type:str] +tailscale_authkey: ENC[AES256_GCM,data:mKOC26CLzqsUrgr9C9AqTA+7KFPlFq4wIh81dZ8lapcY5b1RMp9XTaac5e1YXnFQtC+g8ROo3UR5NiUXepQvnaV2YQ0YwsUcrFN33UcqHgwy87YMrfKaoQ==,iv:xp/ljoD6gqVrZ0gzeYbF9wUOtsDgUIMKd/O1nH3NZpg=,tag:pb0QIiRoHEwfm3rCxO/urA==,type:str] headplane_cookie_secret: ENC[AES256_GCM,data:oXYRG4z16u6HS7zXoWrV2q/HL2o24n4UwVXnQvqBmbY=,iv:itAiy/w6ue4VzqO5xYnvSYN3uCLLmu52dvAAxZ2pCGc=,tag:MEFP171GpSQpAqUFsWY+VA==,type:str] headplane_oidc_client_secret: ENC[AES256_GCM,data:RiEESz1WHYH/smlJKmKFpPoF5HRYp8gHJoKqc49xRRuU9WnQCnmETie/68u37sSVniQmQZSOmXxdmvuuJgP5LMcPdkrQmQE+QX80RQhtTVvg2AhqRaK4V+qfQleD/aJjoIHojeM3UBUi4kYOhDJF0FFTP+qR4oitH6W8/2jxRsc=,iv:hXquOglPhRv+QTVRw5fkY7BWuwLtJxK5tNERGBOOmt8=,tag:2KczveRWoq7iTOdwjO+AUw==,type:str] headplane_headscale_api_key: ENC[AES256_GCM,data:KcwcprV100wfAkn+YM4+1oTfXkmyeAMAbXNpQKf2iIq0VxwqVep+fORrKmHiwDViciDoyxdMiT4scGlmd4vQRsWEHdDpkuH7MRHYu0hQepE3rSSQAMbK,iv:NEhmi2hiOE+uSIRZ4uXOK1UGpN+FQx/NpooWzTgwik8=,tag:KcPsD4gp51ERaFwgfA0BrQ==,type:str] @@ -28,7 +28,7 @@ sops: Wptkf76aP9UpjhgNkxzedRebQPB7ti+UiVqCvLVimtuHcsm/NJPcRg== -----END AGE ENCRYPTED FILE----- recipient: age1hp72xyx2cnd05937e4eww95g5kdtn0wsf9j2nypw330pa69gfdxqn0lpkp - lastmodified: "2026-07-24T23:31:37Z" - mac: ENC[AES256_GCM,data:eYiDMFoicUBJbkJ0d5QCwq+YxaslrQDrSCUufzvZ9PkJJ7wWLgq/9ZwMYZfuTR0iWAQCHyhwOEStuLSUT1p51uLS2cSOQtWZfkBOEdUq9HPzzlOMFxyEh052Dw0fkBNswB9oYBdqOfMX8X4z320JlQNUaR/UQ977dkaLaDCMDt8=,iv:kW2D47/VaOPpbITaGnIwpX3Xrv/4JuDfL7QpQFT3OE8=,tag:v6eUKtQ1lfzDmdxQpMWwrw==,type:str] + lastmodified: "2026-08-21T23:14:15Z" + mac: ENC[AES256_GCM,data:O1wp0QLbfi7hn7+IoKkk+xqGYLX95y9KgVCsjyDjsv6eqiP+RPz5PmEqXapt926HREEPi4vcDW2FbaBMTqtsojbhWMTG0lYmV6zSQ4aDueL9/GmDqdOJDtQdbXhJH9gpAVhwGQzNoRDQ/5wjknDI8GSBVO077aSaaiGbreuuzBQ=,iv:yecrpLaDNFqt7XxHv/KDTXgjOs+5fk/6UXCSxPMuoJ0=,tag:m7co54lvtnZIYqSMX2c3kQ==,type:str] unencrypted_suffix: _unencrypted version: 3.13.2 diff --git a/secrets/terra.yaml b/secrets/terra.yaml index d1af10d..4782053 100644 --- a/secrets/terra.yaml +++ b/secrets/terra.yaml @@ -1,4 +1,4 @@ -tailscale_authkey: ENC[AES256_GCM,data:DgxbMrKTxC//cWd+ZckCbwZ9j/FgjYwNwadU5cWnnPz+nPEFhXLla2TuUE0pJKpcx9pKZCQmxcGtzQvIB+mbgxjgB0X2CRTp4Yy3WR+PBYMghKx36F8KBg==,iv:hJr6O41OTdMa4wkBXEsRUXgV4zWt21h1kx/CWArLU7o=,tag:IEpMIwKBHakvVYEFdEUe7A==,type:str] +tailscale_authkey: ENC[AES256_GCM,data:B2J+PbFv6o3Dt4em0nCV/9a77VxZ2TFnRFj1QDaaUutDjbKtbO+WXID/kE2kVQhnD5xBmhU5inytytbiP8LYKIcNlklqO5c8hJCzAvFx8xpBfpjxcQrojQ==,iv:aPGqQG14LnUabgan1QYUL0Lf6cIJfMDOQS/XBekoxaM=,tag:A0oKUfwvHZvf5e/NDIWSZQ==,type:str] darman_password: ENC[AES256_GCM,data:G3ZM+NMxvKq5twblcBvyC+MiUX+X7nz+s1GqBHBkJQy1YgW4KN6rpbdj10F9jWxHph5dJ9j9fG6L7UlEg5W5zUYeLFdEx2xJGE1fxksch+6BB9FT6LKkhJ7MmbJmhNHYwJOZO3LM1f/oRw==,iv:abQTRe9kRyYj+TL0rtoxM6JFBaIBmxu7y77qt1h4eME=,tag:OgKrB3SUgSNJjmtJyrmh/Q==,type:str] samba_password: ENC[AES256_GCM,data:UkJLUa2hW1iZ++sfJAcg6G1RJMM=,iv:/HbZ9F+GxCydUP50PNBtJknPlmDWh1DAE26N9FJUyb0=,tag:z1cbwUaVdyfwEIU8LINEcw==,type:str] librechat_creds_key: ENC[AES256_GCM,data:e2Ptf41yHu0KxfzjW4DP04CSJfbtdsJ8bwrgJyv9up4/JSCbBzjPFmOi7jsHUL4jT0AGVuHG5w3y+YOil9EeNA==,iv:zneozNSkXsb4Vy/sq21b8HWCKpDkXVTxyLY2Zh0bwP0=,tag:6CxR5sIspbfzHlfdx+45Iw==,type:str] @@ -25,7 +25,7 @@ sops: sHjKfw8VrrmAR4pQf1dsY+wcyh4FsZxhP3Q+QIVq3eCIXS9PeJkGAg== -----END AGE ENCRYPTED FILE----- recipient: age1rfcmu6zh40v4260l9hnf8ajs9vly0s06rx3ey76eu78dp9t7getqyhmkut - lastmodified: "2026-08-19T20:48:49Z" - mac: ENC[AES256_GCM,data:YbsOhLmzynV7S86A9w1XqDEe1/soZPewSrsc+5eR49naQNHUrv2j40J271hMh7r2c+gBZkkHAvq/tQy4USaWPdVn44h6+fPYc11lSlfuIGeIDX6KuXcIw4PdrJr5NgHpQ6+l5XASO0vKOifdqUxENEL8Uu5QJflgg6dA+y6RU94=,iv:ZALmRHzSFx9wzgapZVd27/JRCdTYzVdQsqs0eO9+6ko=,tag:9Un6iiPTxy61yNlms+K8Uw==,type:str] + lastmodified: "2026-08-21T23:14:15Z" + mac: ENC[AES256_GCM,data:gD5C3hYFvIUP7zr4GK40LAtM2sskhGErEzdTxVKaRaPkKNYj+pDYd5uSrBxDHv6W2SAY0TtyN3GyNpD0z5b4yaDxiV8ETUOokkj8f9FlTaZk56S6I/21HvaGXW+Cfi1ioFtNGyJeAhSnNOJ6OE0AA9JfcftSJ8BdTe2LienjHwQ=,iv:LtKFum+f1kaE6/XT4Xj0ZEuFrhSXXsnILMgdebGYNDU=,tag:6DxwNjjgze2ldU1g3SdzzA==,type:str] unencrypted_suffix: _unencrypted version: 3.13.2