# Ahel automations (M1)

Read-only / notify-only scheduled jobs on the always-on M1. Each writes
`<name>.status.json` (read by the Ahel desktop app's Settings → Automations) and pushes
to the ntfy `erebor` topic. The `erebor` topic is a lab wire value (Karl's phone subscribes to it)
and stays as deployed; the `~/.ahel/` paths are post-purge names with compat symlinks on the M1 —
see WORKSPACE.md → "Legacy identifiers".
Deployed to `~/.ahel/automations/`; launchd plists
in `~/Library/LaunchAgents/`. Disable a job by touching `<name>.disabled` (the app's
toggle does this); the app's "Run now" runs `<name>.sh`.

| name | schedule | what it does |
|---|---|---|
| `fleet-digest` | daily 08:00 (`com.ahel.auto-digest`) | gathers pool tok/s, target up-count, firing alerts, Vault %, thermal from Prometheus → asks the 35B for a short brief → ntfy |
| `vault-report` | weekly Sun 09:00 (`com.ahel.auto-vault`) | `df` + top `du` folders on the Vault over SSH (read-only) → ntfy |

**Self-healing is NOTIFY-FIRST, not autonomous.** See `infra/m1/autoheal-notify.py`
(root LaunchDaemon `com.ahel.autoheal`, :9096). Nothing restarts on its own: a
whitelisted `ServiceDown` alert makes Alertmanager POST the autoheal webhook,
which only *publishes* an ntfy notification carrying a "Restart <svc>" button.
The restart runs solely when Karl taps that button (or curls `/restart` with the
shared token) — an explicit human action, through the audited fleet-tools relay.
Whitelist is closed: `pool-main`, `pool-worker`, `bifrost`, `fleet-tools`. The
token lives at `~/.ahel/autoheal.token` (0600), never in this repo.
