From 00a891b1549cc1e6d4913d0bdc40abcb1cbbc5c6 Mon Sep 17 00:00:00 2001 From: dtoro Date: Tue, 2 Jun 2026 00:06:58 +0200 Subject: [PATCH] ssh: fix LXC name parsing in deploy-keys.sh (Lock column edge case) --- HERMES.md | 50 +++++++++++++++++++++++++++++++ bootstrap.sh | 4 +-- containers/105-apps.md | 2 +- hosts/apps.yaml | 4 +-- hosts/arriman.yaml | 2 +- hosts/authentik.yaml | 2 +- hosts/caddy.yaml | 2 +- hosts/claudio-bot.yaml | 2 +- hosts/elementsynapse.yaml | 2 +- hosts/gitea.yaml | 2 +- hosts/haos.yaml | 2 +- hosts/hubris.yaml | 2 +- hosts/jellyfin.yaml | 2 +- hosts/ludo-mini.yaml | 2 +- hosts/mac-mini.yaml | 3 +- hosts/mule-images.yaml | 2 +- hosts/netbird-vps.yaml | 2 +- hosts/nextcloud.yaml | 2 +- hosts/nfs-export.yaml | 2 +- hosts/paperless.yaml | 2 +- hosts/plato.yaml | 2 +- hosts/republic-laptop.yaml | 2 +- hosts/sophia.yaml | 2 +- hosts/zimaos.yaml | 2 +- infrastructure/homelab-context.md | 2 +- inventory.yaml | 2 +- mcp/server.py | 2 +- operations/agent-enrollment.md | 4 +-- ssh/deploy-keys.sh | 11 +++++-- 29 files changed, 90 insertions(+), 32 deletions(-) create mode 100644 HERMES.md diff --git a/HERMES.md b/HERMES.md new file mode 100644 index 0000000..3be27b0 --- /dev/null +++ b/HERMES.md @@ -0,0 +1,50 @@ +# HERMES.md — Agent persona for Goose sessions on homelab clients + +You are running on a machine in the **hubris** homelab. Your primary context +is `/opt/homelab-context/AGENTS.md` — read that first. + +## Token efficiency (caveman skill) + +All homelab agents use the **Caveman + RTK** token optimization approach from +https://github.com/adityahimaone/hermes-agent-rtk-caveman. + +### Before running any CLI command, ask: + +1. **Is there a caveman wrapper equivalent?** Use the wrapper for token-efficient + output. Available wrappers (installed at `~/bin/caveman_wrapper.sh`): + - `~/bin/caveman_wrapper.sh git-status` — compact git status + - `~/bin/caveman_wrapper.sh git-log [n]` — compact git log + - `~/bin/caveman_wrapper.sh lint [target]` — compact lint results + - `~/bin/caveman_wrapper.sh test-results [cmd]` — compact test results + +2. **If no caveman wrapper exists, pipe through `rtk`** to compress output: + ``` + rtk + ``` + RTK (Rust Token Killer) strips redundant whitespace, trims long paths, and + deduplicates repeated lines. This reduces token usage by 60-90% on CLI + operations. + +3. **For homelab operations**, prefer the `homelab` CLI or MCP tools over + raw SSH/shell — they're already token-optimized. + +### Templates + +Caveman templates live at `~/templates/`: +- `git_status.txt` — compact status format +- `git_log.txt` — compact log format +- `lint_results.txt` — compact ESLint format +- `test_results.txt` — compact vitest/jest format + +### When to skip caveman/rtk + +- Interactive commands (editors, prompts) — let human-readable output pass +- Commands with no output — skip entirely +- When you need the exact raw output for post-processing + +## Verification + +```bash +# Check caveman is installed +ls ~/bin/caveman_wrapper.sh && echo "caveman ready" +``` diff --git a/bootstrap.sh b/bootstrap.sh index 239e503..090af9b 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -23,8 +23,8 @@ REPO_HTTPS="${HOMELAB_REPO_URL:-https://git.hubris.network/dtoro/Homelab-Docs.gi CLONE_DIR="${HOMELAB_CONTEXT_DIR:-/opt/homelab-context}" ISSUANCE_URL_NETBIRD="${HOMELAB_ISSUANCE_NETBIRD:-https://secrets.hubris.network/issue}" ISSUANCE_URL_TAILSCALE="${HOMELAB_ISSUANCE_TAILSCALE:-https://secrets.hubris.network/issue}" -MCP_URL="${HOMELAB_MCP_URL:-https://mcp.hubris.network/sse}" -HERMES_MCP_URI="${HOMELAB_HERMES_MCP_URI:-https://mcp.hubris.network/mcp}" +MCP_URL="${HOMELAB_MCP_URL:-https://mcp.hubris.network}" +HERMES_MCP_URI="${HOMELAB_HERMES_MCP_URI:-https://mcp.hubris.network}" HERMES_MODEL="${HOMELAB_HERMES_MODEL:-nousresearch/hermes-4-405b}" WITH_MCP=0 diff --git a/containers/105-apps.md b/containers/105-apps.md index f7fc582..f277e61 100644 --- a/containers/105-apps.md +++ b/containers/105-apps.md @@ -60,7 +60,7 @@ is `dtoro/Homelab-Docs/mcp/server.py`; service unit disabled at the FastMCP layer because mesh+LAN gating is the actual trust boundary. -- Endpoint: `https://mcp.hubris.network/sse` (Caddy → `:9810`). +- Endpoint: `https://mcp.hubris.network` (Caddy → `:9810`). StreamableHTTP transport (POST `/`). - 14 tools registered: `get_host`, `list_services`, `find_service`, `get_topology`, `search_docs`, `get_page`, `get_changelog`, `whoami`, `list_my_secrets` (context); `get_service_status`, `tail_log`, diff --git a/hosts/apps.yaml b/hosts/apps.yaml index 4a47624..aa76910 100644 --- a/hosts/apps.yaml +++ b/hosts/apps.yaml @@ -38,7 +38,7 @@ services_hosted: port: 9810 systemd_unit: homelab-mcp public_host: mcp.hubris.network - endpoint: https://mcp.hubris.network/sse + endpoint: https://mcp.hubris.network note: MCP server. Read-only context + management. Reachable on the LAN via Caddy and from off-LAN via Netbird (192.168.8.0/24 is a network resource routed through hubris). - name: secrets_issuance @@ -51,5 +51,5 @@ services_hosted: age_pubkey: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0 see_also: - containers/105-apps.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/arriman.yaml b/hosts/arriman.yaml index 8f5df9a..a8dd7d6 100644 --- a/hosts/arriman.yaml +++ b/hosts/arriman.yaml @@ -30,5 +30,5 @@ services_hosted: note: jellyseerr / qbit / sab on docker compose see_also: - containers/122-arriman.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/authentik.yaml b/hosts/authentik.yaml index 9329b85..8e98999 100644 --- a/hosts/authentik.yaml +++ b/hosts/authentik.yaml @@ -28,5 +28,5 @@ notes: - 'Also hosts split-horizon dnsmasq: /etc/dnsmasq.d/hubris-split.conf' see_also: - containers/124-authentik.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/caddy.yaml b/hosts/caddy.yaml index 76780e8..12537f7 100644 --- a/hosts/caddy.yaml +++ b/hosts/caddy.yaml @@ -28,5 +28,5 @@ notes: - /etc/caddy is a git checkout of dtoro/caddy-conf see_also: - containers/121-caddy.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/claudio-bot.yaml b/hosts/claudio-bot.yaml index e463c33..a0fb3bd 100644 --- a/hosts/claudio-bot.yaml +++ b/hosts/claudio-bot.yaml @@ -18,5 +18,5 @@ notes: age_pubkey: age1xmkeq968areza2necqyq0065dpeegngzyr6dhagh0n6pl33lccfqe5mqn9 see_also: - containers/123-claudio-bot.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/elementsynapse.yaml b/hosts/elementsynapse.yaml index 32838b4..c2b4f51 100644 --- a/hosts/elementsynapse.yaml +++ b/hosts/elementsynapse.yaml @@ -25,5 +25,5 @@ services_hosted: backend: elementsynapse see_also: - containers/118-elementsynapse.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/gitea.yaml b/hosts/gitea.yaml index 49ee4bf..dde53fb 100644 --- a/hosts/gitea.yaml +++ b/hosts/gitea.yaml @@ -30,5 +30,5 @@ notes: - Bare repos live at /mnt/library/repos/dtoro/*.git see_also: - containers/104-gitea.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/haos.yaml b/hosts/haos.yaml index 9490069..08227bd 100644 --- a/hosts/haos.yaml +++ b/hosts/haos.yaml @@ -23,5 +23,5 @@ services_hosted: backend: haos see_also: - vms/108-haos.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/hubris.yaml b/hosts/hubris.yaml index 89aff22..877d61e 100644 --- a/hosts/hubris.yaml +++ b/hosts/hubris.yaml @@ -31,5 +31,5 @@ services_hosted: age_pubkey: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6 see_also: - hosts/hubris.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/jellyfin.yaml b/hosts/jellyfin.yaml index f77a5e1..b50c70d 100644 --- a/hosts/jellyfin.yaml +++ b/hosts/jellyfin.yaml @@ -27,5 +27,5 @@ services_hosted: backend: jellyfin see_also: - containers/101-jellyfin.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/ludo-mini.yaml b/hosts/ludo-mini.yaml index 341d59c..e3f651b 100644 --- a/hosts/ludo-mini.yaml +++ b/hosts/ludo-mini.yaml @@ -13,5 +13,5 @@ mesh_globals: accepted: - netbird - tailscale -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/mac-mini.yaml b/hosts/mac-mini.yaml index a608ac3..416df8b 100644 --- a/hosts/mac-mini.yaml +++ b/hosts/mac-mini.yaml @@ -5,6 +5,7 @@ name: mac-mini kind: workstation os: macos role: dev +lan_ip: 192.168.8.174 mesh: netbird: fqdn: mac-mini-234-17.netbird.selfhosted @@ -18,5 +19,5 @@ ssh: notes: - Only macOS in the fleet. Bootstrap uses launchd. age_pubkey: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/mule-images.yaml b/hosts/mule-images.yaml index d402715..fd3f4dc 100644 --- a/hosts/mule-images.yaml +++ b/hosts/mule-images.yaml @@ -27,5 +27,5 @@ services_hosted: backend: mule-images see_also: - containers/120-mule-images.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/netbird-vps.yaml b/hosts/netbird-vps.yaml index 8cc3fee..13d026c 100644 --- a/hosts/netbird-vps.yaml +++ b/hosts/netbird-vps.yaml @@ -25,5 +25,5 @@ notes: - Configs rendered by `homelab render-vps-configs` from vps/turnserver.conf.tmpl + vps/management.json.tmpl, with secrets decrypted from secrets/turn-shared-secret.yaml + secrets/netbird-authentik-oidc.yaml on hubris. -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/nextcloud.yaml b/hosts/nextcloud.yaml index d8782cb..16ff6c3 100644 --- a/hosts/nextcloud.yaml +++ b/hosts/nextcloud.yaml @@ -27,5 +27,5 @@ services_hosted: backend: nextcloud see_also: - containers/114-nextcloud.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/nfs-export.yaml b/hosts/nfs-export.yaml index 8381943..28ebe8c 100644 --- a/hosts/nfs-export.yaml +++ b/hosts/nfs-export.yaml @@ -15,5 +15,5 @@ mesh_globals: - tailscale see_also: - containers/102-nfs-export.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/paperless.yaml b/hosts/paperless.yaml index 43a8fa4..1fb426d 100644 --- a/hosts/paperless.yaml +++ b/hosts/paperless.yaml @@ -27,5 +27,5 @@ services_hosted: backend: paperless see_also: - containers/103-paperless.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/plato.yaml b/hosts/plato.yaml index 318da3a..dc7169d 100644 --- a/hosts/plato.yaml +++ b/hosts/plato.yaml @@ -24,5 +24,5 @@ services_hosted: backend: plato see_also: - containers/126-plato.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/republic-laptop.yaml b/hosts/republic-laptop.yaml index 1962ddd..f975a78 100644 --- a/hosts/republic-laptop.yaml +++ b/hosts/republic-laptop.yaml @@ -15,5 +15,5 @@ mesh_globals: - tailscale ssh: user: dtoro -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/sophia.yaml b/hosts/sophia.yaml index 62d08c0..64774aa 100644 --- a/hosts/sophia.yaml +++ b/hosts/sophia.yaml @@ -20,5 +20,5 @@ mounts: - /mnt/library see_also: - containers/119-sophia.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/hosts/zimaos.yaml b/hosts/zimaos.yaml index ff105a7..7020217 100644 --- a/hosts/zimaos.yaml +++ b/hosts/zimaos.yaml @@ -22,5 +22,5 @@ services_hosted: backend: zimaos see_also: - vms/100-zimaos.md -mcp_endpoint: https://mcp.hubris.network/sse +mcp_endpoint: https://mcp.hubris.network secrets_issuance_endpoint: https://secrets.hubris.network/issue diff --git a/infrastructure/homelab-context.md b/infrastructure/homelab-context.md index 8dc40ec..b839b03 100644 --- a/infrastructure/homelab-context.md +++ b/infrastructure/homelab-context.md @@ -16,7 +16,7 @@ page is the architecture reference. | Per-client clone | every enrolled client | `/opt/homelab-context/` | Read by `homelab` CLI, MCP server, claudio-bot | | `homelab` CLI | every enrolled client | `/usr/local/bin/homelab` → `/opt/homelab-context/bin/homelab` (symlink) | Operator surface for enroll/secret/ssh/pct | | Per-client age key | every enrolled client | `/etc/age/key.txt` (0600 root) | Decrypts SOPS-encrypted secrets the client is a recipient on | -| MCP server | [apps (105)](../containers/105-apps.md) | `homelab-mcp.service` on port 9810 (https://mcp.hubris.network/sse) | 14 tools: 8 context (get_host, search_docs, …) + 5 read-only management (get_service_status, tail_log, …) + list_my_secrets | +| MCP server | [apps (105)](../containers/105-apps.md) | `homelab-mcp.service` on port 9810 (https://mcp.hubris.network) | 14 tools: 8 context (get_host, search_docs, …) + 5 read-only management (get_service_status, tail_log, …) + list_my_secrets | | Secrets-issuance | [apps (105)](../containers/105-apps.md) | `secrets-issuance.service` on port 9820 (https://secrets.hubris.network/issue) | Generates per-client age keypair on first bootstrap; idempotent; admin-token-gated `/revoke` | | Sync timer | every enrolled client | `homelab-context-sync.timer` (Linux) / `network.hubris.homelab-context-sync.plist` (macOS) | `git pull --ff-only` every 5 min | | Encrypted secrets | `dtoro/Homelab-Docs` | `secrets/*.yaml` (SOPS+age) | Recipients declared in `.sops.yaml` | diff --git a/inventory.yaml b/inventory.yaml index b76f253..00c79a0 100644 --- a/inventory.yaml +++ b/inventory.yaml @@ -88,7 +88,7 @@ services: port: 9810 systemd_unit: homelab-mcp public_host: mcp.hubris.network - endpoint: https://mcp.hubris.network/sse + endpoint: https://mcp.hubris.network note: MCP server. Read-only context + management. Reachable on the LAN via Caddy and from off-LAN via Netbird (192.168.8.0/24 is a network resource routed through hubris). diff --git a/mcp/server.py b/mcp/server.py index f895991..48897b2 100755 --- a/mcp/server.py +++ b/mcp/server.py @@ -333,4 +333,4 @@ def ping_service(service: str) -> dict: if __name__ == "__main__": - mcp.run(transport="sse") + mcp.run(transport="streamable_http") diff --git a/operations/agent-enrollment.md b/operations/agent-enrollment.md index 1003557..6b0263c 100644 --- a/operations/agent-enrollment.md +++ b/operations/agent-enrollment.md @@ -20,7 +20,7 @@ operational reference is here. | OS is Linux or macOS | bootstrap detects via `uname -s` | `uname -s` | | On the mesh (Netbird or Tailscale) **or** on the LAN | issuance is gated to mesh + LAN subnets. **For Netbird: use a setup-key, not interactive auth** — see "Getting onto Netbird" below. | `netbird status` / `tailscale status` | | `git`, `python3`, `python3-yaml`, `age`, `sops` | bootstrap preflight; `homelab` CLI imports yaml | See per-OS commands below | -| Can resolve `*.hubris.network` | bootstrap calls `https://secrets.hubris.network/issue` and writes `https://mcp.hubris.network/sse` | `dig +short mcp.hubris.network` (should return `192.168.8.175`) | +| Can resolve `*.hubris.network` | bootstrap calls `https://secrets.hubris.network/issue` and writes `https://mcp.hubris.network` | `dig +short mcp.hubris.network` (should return `192.168.8.175`) | ### Hostname mismatch is the most common bootstrap failure @@ -83,7 +83,7 @@ If DNS isn't an option at all, override the URLs at bootstrap time: sudo HOMELAB_GITEA_TOKEN=... \ HOMELAB_REPO_URL=http://192.168.8.121:3000/dtoro/Homelab-Docs.git \ HOMELAB_ISSUANCE_NETBIRD=http://192.168.8.205:9820/issue \ - HOMELAB_MCP_URL=http://192.168.8.205:9810/sse \ + HOMELAB_MCP_URL=http://192.168.8.205:9810 \\ bash /tmp/bootstrap.sh --with-mcp ``` diff --git a/ssh/deploy-keys.sh b/ssh/deploy-keys.sh index 4050944..790a8a8 100644 --- a/ssh/deploy-keys.sh +++ b/ssh/deploy-keys.sh @@ -49,8 +49,15 @@ echo " hubris: $added key(s) added" echo "" echo "= Deploying SSH keys to LXCs =" -# pct list output: VMID Status Lock Name -pct list | tail -n +2 | while read -r vmid status _ name; do +# pct list output: VMID Status Lock Name (Lock may be empty) +# Use awk to grab VMID, Status, and Name (last field) +pct list | tail -n +2 | while read -r line; do + vmid=$(echo "$line" | awk '{print $1}') + status=$(echo "$line" | awk '{print $2}') + name=$(echo "$line" | awk '{print $NF}') + # Skip header if any + [ "$vmid" = "VMID" ] && continue + [ -z "$vmid" ] && continue if [ "$status" != "running" ]; then echo " SKIP $name ($vmid): status=$status" continue