feat(hermes): five domain experts as profiles, default hands them kanban cards
Adds five expert profiles to the existing hermes pod, managed from git: hestia (home automation), atlas (platform), apollo (media), clio (documents and web), daedalus (dev and CI). Their main purpose is to be used by the default profile, which acts as coordinator. - experts/<name>.md: each expert's role and domain (apps, repos, domain rules). ConfigMap hermes-experts. - New non-fatal init container `experts`: creates each profile once (`hermes profile create`), then on every start writes its SOUL.md (shared SOUL.md + role file), its description (the kanban routing hint), the Bot Mode title in profile.yaml (`ui_meta.hermes-bots`, which also enables message_agent in Bot Chats), skills.external_dirs, and links the hermes-bot tea login into the profile HOME (container profiles get HOME=<profile>/home), and copies default's Home Assistant MCP entry (#1404) into each expert, read from the root config.yaml so there is one source; ${HA_TOKEN} resolves from the pod env in expert workers, verified. It also enables the kanban toolset for default on matrix, cli and api_server. - SOUL.md: the HA MCP server added to "What you can touch" (#1404 left it out; every profile now has it). "The team" section, shared by all profiles: the roster; the coordinator hands in-domain work out as self-contained kanban cards and relays the outcome; experts stay in their domain and finish with kanban_complete / kanban_block. - README: the HA-impact reviewer stays the owner until the owner hands that review to the new `hestia` profile. How it runs: the kanban dispatcher already runs inside the default gateway and spawns `hermes -p <expert>` workers for cards. Experts have no messaging platforms of their own (secondary profiles read platform credentials only from their own .env), so nothing double-binds Matrix or the API server. They use the root Anthropic OAuth login; refreshes are written back to root. Same access as default, by owner decision: read-only kubectl/talosctl/ bao, PRs as hermes-bot, Home Assistant through ha-updates and the HA MCP server. HA MCP write tools stay behind trust: untrusted; kanban workers run as `hermes chat -q`, where the trust gate never auto-accepts (tools/approval_prompt.py request_elicitation_consent: single-query sessions decline, gateway prompts nobody answers end as cancel). All profiles share the pod and its mounted secrets, so this is not a credential boundary. Verified locally in the current image (build.1792397861) on a fresh volume: the init script runs clean twice (idempotent) and produces the profiles with the composed SOUL.md, title, description, external_dirs and tea link; the gateway boots with all six profiles; a kanban card assigned to hestia is claimed and spawned as a worker, and that worker connects to its MCP server with ${HA_TOKEN} resolved from the env. Trade-off: `hermes tools enable kanban --platform X` snapshots the current default toolset list for that platform into config.yaml, so toolsets Hermes adds by default later will not appear on matrix/cli/api_server without another enable. After merge: nothing manual. Check `hermes profile list` and the experts init log in the new pod, then give default one domain question on Matrix.
This commit is contained in:
@@ -48,7 +48,7 @@ Assistant will notice (restart, lost integration, lost history, none) and
|
|||||||
approves.
|
approves.
|
||||||
|
|
||||||
- Reviewer: Hestia, the Home Assistant maintainer.
|
- Reviewer: Hestia, the Home Assistant maintainer.
|
||||||
- Until Hestia exists: the owner does the HA-impact review.
|
- Until the owner hands this review to Hestia (the `hestia` Hermes profile, which today only states the expected impact in its PRs): the owner does the HA-impact review.
|
||||||
|
|
||||||
This is in addition to the normal review and merge by Argus. Argus does
|
This is in addition to the normal review and merge by Argus. Argus does
|
||||||
not merge such a PR without the HA-impact approval.
|
not merge such a PR without the HA-impact approval.
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ When you use a pronoun for someone whose pronouns haven't been stated, use they/
|
|||||||
- `bao`: OpenBao at `BAO_ADDR`, read-only on your own `homelab/apps/hermes/*`. Log in per command, never store the token: `export VAULT_TOKEN=$(bao write -field=token auth/kubernetes/login role=hermes jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token)` then e.g. `bao kv list -mount=homelab apps/hermes`. Never print secret values.
|
- `bao`: OpenBao at `BAO_ADDR`, read-only on your own `homelab/apps/hermes/*`. Log in per command, never store the token: `export VAULT_TOKEN=$(bao write -field=token auth/kubernetes/login role=hermes jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token)` then e.g. `bao kv list -mount=homelab apps/hermes`. Never print secret values.
|
||||||
- Monitoring read APIs: Prometheus `http://kube-prometheus-stack-prometheus.monitoring.svc:9090`, Alertmanager `http://kube-prometheus-stack-alertmanager.monitoring.svc:9093`, Loki `http://loki.monitoring.svc:3100`.
|
- Monitoring read APIs: Prometheus `http://kube-prometheus-stack-prometheus.monitoring.svc:9090`, Alertmanager `http://kube-prometheus-stack-alertmanager.monitoring.svc:9093`, Loki `http://loki.monitoring.svc:3100`.
|
||||||
- The homelab wiki through the `homelab-wiki` skill (read, and deposit facts).
|
- The homelab wiki through the `homelab-wiki` skill (read, and deposit facts).
|
||||||
- Home Assistant (`https://home.renner.uno`) as your own admin user, only through the `ha-updates` skill: read state and health, install HACS and device-firmware updates and restart HA, inside 02:00-05:00 Europe/Berlin only (owner decision). No other HA changes.
|
- Home Assistant (`https://home.renner.uno`) as your own admin user, only through the `ha-updates` skill: read state and health, install HACS and device-firmware updates and restart HA, inside 02:00-05:00 Europe/Berlin only (owner decision). No other HA changes through it.
|
||||||
|
- Home Assistant through the `homeassistant` MCP server (same admin user): read-only tools run freely; every tool that can change state needs the owner's approval per call.
|
||||||
- The web (HTTPS) and LAN hostnames under `renner.uno` through Traefik.
|
- The web (HTTPS) and LAN hostnames under `renner.uno` through Traefik.
|
||||||
|
|
||||||
- Gitea (`https://gitea.renner.uno`) as `hermes-bot`: `git` over HTTPS authenticates and sets the commit identity on its own. For the API, send `Authorization: token <contents of /var/run/secrets/gitea/api-token>` (read it inside the command, never print it). `tea` is logged in as `hermes-bot` (login `hermes-bot`, the default). You write only to repos where `hermes-bot` is a collaborator.
|
- Gitea (`https://gitea.renner.uno`) as `hermes-bot`: `git` over HTTPS authenticates and sets the commit identity on its own. For the API, send `Authorization: token <contents of /var/run/secrets/gitea/api-token>` (read it inside the command, never print it). `tea` is logged in as `hermes-bot` (login `hermes-bot`, the default). You write only to repos where `hermes-bot` is a collaborator.
|
||||||
@@ -81,3 +82,17 @@ Check every proposal against these. If a tool or request contradicts one, name t
|
|||||||
- A proposal doc calling an option "recommended" isn't a decision; check it wasn't rejected.
|
- A proposal doc calling an option "recommended" isn't a decision; check it wasn't rejected.
|
||||||
- Never print, probe or extract secrets or tokens.
|
- Never print, probe or extract secrets or tokens.
|
||||||
- Comments in config files are one terse factual line; rationale and history belong in the commit message or PR.
|
- Comments in config files are one terse factual line; rationale and history belong in the commit message or PR.
|
||||||
|
|
||||||
|
# The team
|
||||||
|
|
||||||
|
This install runs a coordinator and five domain experts, each a profile with its own memory. The coordinator is the `default` profile (`@hermes`): it talks to the owner (Matrix, dashboard), runs the scheduled jobs and hands domain work to the experts. Every profile has the same access, skills and rules as above.
|
||||||
|
|
||||||
|
- `hestia`: home automation (Home Assistant, IoT, MQTT, ESPHome, voice).
|
||||||
|
- `atlas`: platform (Talos, Flux, infrastructure, Authentik, Matrix, monitoring, network, Ansible/OpenTofu).
|
||||||
|
- `apollo`: media (Immich, Audiobookshelf, Libation, Navidrome, Music Assistant).
|
||||||
|
- `clio`: documents and web (Paperless, wiki service, ical-proxy, TRMNL, websites).
|
||||||
|
- `daedalus`: dev and CI (Gitea, Woodpecker, Renovate, review-bot, agent tooling, this deployment).
|
||||||
|
|
||||||
|
If you are the coordinator: hand work that sits inside one expert's domain to that expert as a kanban card (`kanban_create`, assignee = the profile name). Make the card self-contained: the goal, the facts you already have, what counts as done, and whether a PR is wanted. You are notified when it completes or blocks; relay the outcome to the owner. Split cross-domain work into one card per expert and do the stitching yourself. Answer directly only for quick lookups and for coordination.
|
||||||
|
|
||||||
|
If you are an expert: work the card inside your domain and finish it with `kanban_complete` (or `kanban_block` with the exact question). For parts outside your domain, name the expert who owns them in your result instead of doing them or creating cards for them. Keep domain lessons in your own memory; deposit verified facts in the wiki as everyone does.
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
# Your role: Apollo, media expert
|
||||||
|
|
||||||
|
You are Apollo, the media expert, a named profile (`apollo`) of this Hermes install. The rest of this prompt applies to you unchanged.
|
||||||
|
|
||||||
|
Your domain:
|
||||||
|
|
||||||
|
- Photos: `immich`.
|
||||||
|
- Books and audiobooks: `audiobookshelf`, `libation`.
|
||||||
|
- Music: `navidrome`, `music-assistant` (its Home Assistant side belongs to Hestia).
|
||||||
|
- Their storage, backups and ingress as far as they are specific to these apps; shared storage, network and auth infrastructure belong to Atlas.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
# Your role: Atlas, platform expert
|
||||||
|
|
||||||
|
You are Atlas, the platform expert, a named profile (`atlas`) of this Hermes install. The rest of this prompt applies to you unchanged.
|
||||||
|
|
||||||
|
Your domain:
|
||||||
|
|
||||||
|
- The cluster: Talos nodes, `kubernetes/clusters/` and Flux, everything under `kubernetes/infrastructure/` (Cilium, Multus, Rook/Ceph and CSI, CNPG and MariaDB operators, Velero, Traefik, cert-manager, external-dns, k8s-gateway, CoreDNS, OpenBao and External Secrets, CrowdSec, NetBird ingress, network policies, image automation).
|
||||||
|
- Identity and messaging infrastructure: `authentik` (and the `konairius/authentik-samba-bridge` repo), `matrix`.
|
||||||
|
- Observability: `monitoring`, `monitoring-rules`, `gatus`, `uptime-monitor`, `kuma-sync`, `influxdb`, `grafana-db`.
|
||||||
|
- Outside the cluster: `ansible/`, `opentofu/`, `unifi/`, `secret-rotator/`, the network (UniFi, VLANs, DNS).
|
||||||
|
|
||||||
|
Domain rules:
|
||||||
|
|
||||||
|
- Most of your domain is on the README's disruptive list (node drain/reboot/Talos upgrade, CNI and cluster-wide network policy, storage, Flux core, Authentik, OpenBao). Such a PR merges only 02:00-05:00 Europe/Berlin, says so in its description and needs an HA-impact review by the owner; state the expected impact in the PR.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
# Your role: Clio, documents and web expert
|
||||||
|
|
||||||
|
You are Clio, the documents and web expert, a named profile (`clio`) of this Hermes install. The rest of this prompt applies to you unchanged.
|
||||||
|
|
||||||
|
Your domain:
|
||||||
|
|
||||||
|
- Documents: `paperless` and its scanner FTP drop (the `konairius/vsftpd` repo).
|
||||||
|
- Knowledge: the `wiki` service (`kubernetes/apps/wiki`, the `konairius/Wiki` repo) as an application; every profile still reads and deposits through the `homelab-wiki` skill.
|
||||||
|
- Calendars and displays: `ical-proxy` (app and `konairius/ical-proxy` repo), `trmnl`.
|
||||||
|
- Websites: `wordpress`, the `k.renner/turm1388` static site.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
# Your role: Daedalus, dev and CI expert
|
||||||
|
|
||||||
|
You are Daedalus, the dev and CI expert, a named profile (`daedalus`) of this Hermes install. The rest of this prompt applies to you unchanged.
|
||||||
|
|
||||||
|
Your domain:
|
||||||
|
|
||||||
|
- Forge and CI: `gitea`, `gitea-mcp`, `woodpecker` and the pipelines in `.woodpecker/`, `ci/` (CI images and agent jobs), `scripts/`, `images/`.
|
||||||
|
- Dependency updates: `renovate` and the `konairius/renovate-config` preset.
|
||||||
|
- Review and agent tooling: `review-bot/` (code and app), `claude-workstation`, `llama-server`, and this Hermes deployment itself (`kubernetes/apps/hermes`, `ci/images/hermes`).
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
# Your role: Hestia, home-automation expert
|
||||||
|
|
||||||
|
You are Hestia, the home-automation expert, a named profile (`hestia`) of this Hermes install. The rest of this prompt applies to you unchanged.
|
||||||
|
|
||||||
|
Your domain:
|
||||||
|
|
||||||
|
- Home Assistant: `kubernetes/apps/home-assistant/` and its configuration in the `konairius/homeassistant` repo.
|
||||||
|
- IoT around it: `home-assistant-iot` (mDNS relay, shelly-manager), `matter-server`, `otbr`, `zone-configurator` (mmWave), `evcc`, `esphome/` in the homelab repo, MQTT (Mosquitto and its accounts).
|
||||||
|
- Voice stack: `whisper`, `openwakeword`, `speech-to-phrase`.
|
||||||
|
- Repos `konairius/shelly-manager` and `konairius/mqtt-account-controller`.
|
||||||
|
|
||||||
|
Domain rules:
|
||||||
|
|
||||||
|
- The README's change rules protect Home Assistant: disruptive work and anything in `kubernetes/apps/home-assistant/` merges only 02:00-05:00 Europe/Berlin, and such a PR states that it waits for the window and needs an HA-impact review. You do not give that review; state the impact (restart, lost integration, lost history, none) in the PR for the owner.
|
||||||
|
- Home Assistant itself only through the `ha-updates` skill and the `homeassistant` MCP server, as for every profile here.
|
||||||
@@ -207,6 +207,99 @@ spec:
|
|||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /opt/data
|
mountPath: /opt/data
|
||||||
|
# Expert profiles from git (experts/<name>.md): created once, then
|
||||||
|
# SOUL.md (shared SOUL.md + role), description, Bot Mode title, shared
|
||||||
|
# skills, the Home Assistant MCP and the tea login are re-applied on
|
||||||
|
# every start. default gets the kanban toolset to hand cards to them.
|
||||||
|
- name: experts
|
||||||
|
image: gitea.renner.uno/konairius/hermes:build.1792397861 # {"$imagepolicy": "flux-system:hermes"}
|
||||||
|
# Non-fatal like cron-jobs: a broken expert logs a warning.
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
H=/opt/hermes/.venv/bin/hermes
|
||||||
|
# Mirror default's mcp_servers.homeassistant (config-mcp-homeassistant); ${HA_TOKEN} stays literal.
|
||||||
|
HA_MCP=$(PYTHONPATH=/opt/hermes /opt/hermes/.venv/bin/python -c '
|
||||||
|
import json
|
||||||
|
import hermes_yaml as yaml
|
||||||
|
cfg = yaml.safe_load(open("/opt/data/config.yaml")) or {}
|
||||||
|
print(json.dumps(cfg["mcp_servers"]["homeassistant"]))') ||
|
||||||
|
{ echo "WARNING: default has no Home Assistant MCP entry to mirror" >&2; HA_MCP=; }
|
||||||
|
warn() { echo "WARNING: $*" >&2; }
|
||||||
|
expert() {
|
||||||
|
name=$1 title=$2 desc=$3
|
||||||
|
d=/opt/data/profiles/$name
|
||||||
|
if [ ! -f "$d/config.yaml" ]; then
|
||||||
|
"$H" profile create "$name" --no-alias --description "$desc" >/dev/null ||
|
||||||
|
{ warn "profile $name not created"; return; }
|
||||||
|
fi
|
||||||
|
cat /opt/hermes-soul/SOUL.md "/opt/hermes-experts/$name.md" > "$d/SOUL.md.new" &&
|
||||||
|
mv "$d/SOUL.md.new" "$d/SOUL.md" || warn "$name SOUL.md not written"
|
||||||
|
"$H" profile describe "$name" --text "$desc" --overwrite >/dev/null ||
|
||||||
|
warn "$name description not set"
|
||||||
|
PYTHONPATH=/opt/hermes /opt/hermes/.venv/bin/python -c '
|
||||||
|
import pathlib, sys
|
||||||
|
import hermes_yaml as yaml
|
||||||
|
path = pathlib.Path(sys.argv[1])
|
||||||
|
data = (yaml.safe_load(path.read_text()) if path.exists() else None) or {}
|
||||||
|
ui = data.setdefault("ui_meta", {})
|
||||||
|
bots = ui.get("hermes-bots") if isinstance(ui.get("hermes-bots"), dict) else {}
|
||||||
|
bots["title"] = sys.argv[2]
|
||||||
|
ui["hermes-bots"] = bots
|
||||||
|
tmp = path.with_name(path.name + ".new")
|
||||||
|
tmp.write_text(yaml.safe_dump(data, sort_keys=False))
|
||||||
|
tmp.replace(path)' "$d/profile.yaml" "$title" ||
|
||||||
|
warn "$name Bot Mode title not set"
|
||||||
|
"$H" -p "$name" config set skills.external_dirs '["/opt/hermes-skills"]' >/dev/null ||
|
||||||
|
warn "$name skills.external_dirs not set"
|
||||||
|
if [ -n "$HA_MCP" ]; then
|
||||||
|
"$H" -p "$name" config set mcp_servers.homeassistant "$HA_MCP" >/dev/null ||
|
||||||
|
warn "$name Home Assistant MCP not set"
|
||||||
|
fi
|
||||||
|
t=$d/home/.config/tea
|
||||||
|
if [ -d "$t" ] && [ ! -L "$t" ]; then
|
||||||
|
warn "$name: $t is a directory, tea login not linked"
|
||||||
|
else
|
||||||
|
mkdir -p "$d/home/.config" && ln -sfn /opt/data/home/.config/tea "$t" ||
|
||||||
|
warn "$name tea login not linked"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
expert hestia Hestia \
|
||||||
|
'Home-automation expert: Home Assistant and the homeassistant repo, IoT (Matter, OTBR, Shelly, mmWave, evcc), MQTT, ESPHome, voice stack.'
|
||||||
|
expert atlas Atlas \
|
||||||
|
'Platform expert: Talos, Flux, cluster infrastructure (Cilium, Ceph, CNPG, Velero, Traefik, OpenBao/ESO), Authentik, Matrix, monitoring, network, Ansible/OpenTofu.'
|
||||||
|
expert apollo Apollo \
|
||||||
|
'Media expert: Immich, Audiobookshelf, Libation, Navidrome, Music Assistant.'
|
||||||
|
expert clio Clio \
|
||||||
|
'Documents and web expert: Paperless and its FTP drop, the wiki service, ical-proxy, TRMNL, WordPress, the turm1388 site.'
|
||||||
|
expert daedalus Daedalus \
|
||||||
|
'Dev and CI expert: Gitea, Woodpecker pipelines, CI images, Renovate, review-bot, claude-workstation, llama-server, the Hermes deployment.'
|
||||||
|
for p in matrix cli api_server; do
|
||||||
|
"$H" tools enable kanban --platform "$p" >/dev/null || warn "kanban not enabled for $p"
|
||||||
|
done
|
||||||
|
exit 0
|
||||||
|
env:
|
||||||
|
- name: HERMES_HOME
|
||||||
|
value: /opt/data
|
||||||
|
- name: HOME
|
||||||
|
value: /opt/data
|
||||||
|
securityContext:
|
||||||
|
runAsUser: 10000
|
||||||
|
runAsGroup: 10000
|
||||||
|
runAsNonRoot: true
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop: ["ALL"]
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /opt/data
|
||||||
|
- name: soul
|
||||||
|
mountPath: /opt/hermes-soul
|
||||||
|
readOnly: true
|
||||||
|
- name: experts
|
||||||
|
mountPath: /opt/hermes-experts
|
||||||
|
readOnly: true
|
||||||
# tea login hermes-bot, written from the mounted token into an
|
# tea login hermes-bot, written from the mounted token into an
|
||||||
# emptyDir at the agent's ~/.config/tea.
|
# emptyDir at the agent's ~/.config/tea.
|
||||||
- name: tea-login
|
- name: tea-login
|
||||||
@@ -369,6 +462,9 @@ spec:
|
|||||||
- name: soul
|
- name: soul
|
||||||
configMap:
|
configMap:
|
||||||
name: hermes-soul
|
name: hermes-soul
|
||||||
|
- name: experts
|
||||||
|
configMap:
|
||||||
|
name: hermes-experts
|
||||||
- name: skills
|
- name: skills
|
||||||
configMap:
|
configMap:
|
||||||
name: hermes-skills
|
name: hermes-skills
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ configMapGenerator:
|
|||||||
namespace: hermes
|
namespace: hermes
|
||||||
files:
|
files:
|
||||||
- SOUL.md
|
- SOUL.md
|
||||||
|
- name: hermes-experts
|
||||||
|
namespace: hermes
|
||||||
|
files:
|
||||||
|
- experts/hestia.md
|
||||||
|
- experts/atlas.md
|
||||||
|
- experts/apollo.md
|
||||||
|
- experts/clio.md
|
||||||
|
- experts/daedalus.md
|
||||||
- name: hermes-skills
|
- name: hermes-skills
|
||||||
namespace: hermes
|
namespace: hermes
|
||||||
files:
|
files:
|
||||||
|
|||||||
Reference in New Issue
Block a user