Agent ownership — Rootbeet & Beetroot

Last updated: 18 Oct 2026 (by Rootbeet)

Two Hermes agents, two failure domains. The rule is one job, one owner — never the same schedule on both boxes, because that means two sets of alerts and two divergent states.

The two agents

RootbeetBeetroot
Wherejoe-host VM 101 (192.168.0.182, tailnet 100.96.95.65)HA box, Docker add-on app_local_hermes_standby (192.168.0.155)
Home/home/joe/.hermes/config/hermes-standby (bind-mounted into the add-on)
Dies whenVM/disk fails, gateway stopsHA host fails, container stops
Roledoes the work: homelab, files, docker, backups, trackerswatches Rootbeet, and holds the HA-local checks

Ownership

Rootbeet owns (all of it, unless listed below): trackers and digests, backups (B2 immich / system / HA), disk and pool watchdogs, docker stacks, Paperless, Home Assistant integration-level checks (ha-health-watch), Zigbee watchdogs (zigbee-health-watch, zigbee-weekly-report, zigbee-budget-calibrate, zigbee-function-audit, zigbee-availability-publish), finance, email, calendar, reminders.

Beetroot owns:

  • peer-watch — every 5 min: is Rootbeet’s gateway up and answering? Auto-restarts the systemd unit when it is genuinely down (max 3 per 6 h), re-alerts hourly while down. Delivers to Telegram 5833220727 explicitly (set 18 Oct 2026 — origin is not reliable for a job created from the CLI).
  • cloudflared-ha-watch — Cloudflare tunnel health from the HA side.
  • Anything that must still speak when joe-host is dead.

Paused on Beetroot 18 Oct 2026 (duplicates of Rootbeet jobs): zigbee-health-watch, zigbee-weekly-report, zigbee-budget-calibrate, zigbee-function-audit, zigbee-availability-publish, ha-health-watch. Reversible with hermes cron resume <id>.

Cross-checks

  • Beetroot → Rootbeet: peer-watch.py, every 5 min. Judges on three signals: unit state, HTTP answer on :8642, and disk. Restarts only when systemd says the unit is not running — a wedged-but-alive gateway is alerted, not restarted.
  • Rootbeet → Beetroot: beetroot-watch.py (Rootbeet), every 30 min over SSH to the HA host. Checks the add-on container is running and peer-watch.json is under 45 minutes old. Silent when healthy.

Neither side restarts the other unless it is genuinely not running, so a working agent is never interrupted mid-task.

Rules both agents follow

  1. One job, one owner. Adding a schedule means checking the other side first.
  2. Alerts go to Joe’s Telegram, never to another agent’s chat. An alert that only lands in a log is not an alert.
  3. no_agent scripts only — no LLM in a scheduled path (Joe’s standing rule).
  4. Never restart the other agent’s gateway while it is answering; only when it is stopped.
  5. Secrets never cross: each agent reads its own config.yaml / .env. No credentials in scripts, chat, or this file.

Noise rules (added 19 Oct 2026)

Telegram is for actions and bad news. A script that confirms it did its routine job should say nothing at all.

  • Silent on success. “Synced”, “pruned 0 files”, “reconciled 0 incidents”, “brain updated” are not messages. Only print when something changed or failed.
  • No alert/all-clear pair for a transient. ha-health-watch now suppresses the “resolved” line unless the problem lasted ≥45 minutes — otherwise one swap spike produces two pings.
  • Checkpoints are quiet. b2-immich-status reports in full at 09:00; its 15:00 and 21:00 runs only speak if the gap is >20 files, B2 is unreachable, or last night’s backup failed.
  • Watchdogs stay loud. Anything that is silent-unless-broken keeps its Telegram delivery — moving those to local would hide the very alert they exist for. The test is “would a human need to act?”, not “is it scheduled often”.

Host access

  • Beetroot → joe-host: SSH as joe using /config/hermes-standby/.ssh/id_ed25519 (authorised in /home/joe/.ssh/authorized_keys, entry comment hermes-standby@ha).
  • Rootbeet → HA host: SSH root@192.168.0.155 (key-based, already present).
  • The add-on’s CLI needs HERMES_HOME set explicitly: docker exec -e HERMES_HOME=/config/hermes-standby app_local_hermes_standby hermes cron list — without it the CLI reads /root/.hermes and reports “No scheduled jobs”.