From 0fbb5b4cfcc852ca5a5278edd984e53713f2e01d Mon Sep 17 00:00:00 2001 From: jazzymc Date: Sat, 7 Nov 2026 10:38:13 +0000 Subject: [PATCH] Herdr: schedule the self-heal, document file drops and the shot helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The self-heal script written on 2026-11-05 was never scheduled — it had run once, by hand. The hc binary had disappeared with no reboot at 25 days uptime, so the boot-time restore in the go file is not sufficient on its own. Now runs every 10 minutes, from the go file's crontab line because that line replaces the whole crontab at boot. Also documents hc put as the transport for Mac screenshots into a session here (verified end to end), the RAM-backed drop location, $HC_TERMINAL_ID, the new shot helper, and what reaching the Mac from this box still needs. Separately: the crontab comment's claimed hourly git-ref update of the AMPECO backend checkout corresponds to no actual job and has not been running. Co-Authored-By: Claude Opus 5 --- docs/14-HERDR-REMOTE-MACHINES.md | 86 ++++++++++++++++++++++++++++++++ docs/CHANGELOG.md | 11 ++++ 2 files changed, 97 insertions(+) diff --git a/docs/14-HERDR-REMOTE-MACHINES.md b/docs/14-HERDR-REMOTE-MACHINES.md index 62747bd..637684a 100644 --- a/docs/14-HERDR-REMOTE-MACHINES.md +++ b/docs/14-HERDR-REMOTE-MACHINES.md @@ -140,6 +140,92 @@ script are at `/boot/config/go.bak-*`. The binary restore was verified without rebooting, by deleting `/root/.local/bin/herdr-core` and running the line by hand: it came back `-rwxr-xr-x` reporting `hc 0.1.17`. +### A boot-time restore is not enough on its own + +On 2026-11-05 the `hc` binary disappeared with **no reboot**, at 25 days uptime, +so the `go` block above cannot be the only guard. +`/boot/config/herdr/herdr-selfheal.sh` restores the binary from the flash stash, +re-links both `/usr/local/bin` names and re-enables linger if it has lapsed. It +is idempotent and silent when nothing is wrong. + +The script existed from 2026-11-05 but **was not scheduled anywhere** — it had +run exactly once, by hand. Scheduled 2026-11-07, every 10 minutes: + +``` +*/10 * * * * /bin/sh /boot/config/herdr/herdr-selfheal.sh +``` + +It belongs in the `printf ... | crontab -` line in `/boot/config/go`, because +that line **replaces the whole crontab** at boot: an entry added with +`crontab -e` would be wiped at the next restart. `/bin/sh` is required because +`/boot` is vfat and carries no execute bit. The live crontab and what the `go` +file will set at boot were diffed and are identical, so there is no drift. + +Verified without a reboot: deleting `/usr/local/bin/hc` took `hc` off `PATH`, +the script relinked it, logged `relinked /usr/local/bin/hc`, and `hc --version` +reported `0.1.17` again. The core and its 8 live terminals were unaffected — +the running core is already a process and does not re-resolve the symlink. + +## File drops (`hc put`) and the `shot` helper + +`hc put ` sends a **local** file to the terminal's machine and +prints where it landed. It takes `machine/t7`, so the same command works whether +the terminal is here or on the Mac — which makes it the transport for getting a +Mac screenshot into a session running on this box: + +``` +hc put unraid/t13 shot.png +-> {"bytes":67,"path":"/root/.local/state/herdr-core/default/files/t13/shot.png"} +``` + +| Property | Value | +|----------|-------| +| Destination | `/root/.local/state/herdr-core//files//` | +| Survives a reboot | **no** — `/root/.local/state` is the RAM rootfs | +| Relocatable | no — 0.1.17 honours no `XDG_*` override for it | +| Extension | preserved, so `.png` stays `.png` | + +Right for screenshots, wrong for anything large or worth keeping. + +Inside a terminal, `$HC_TERMINAL_ID` is that terminal's own address and +`$HC_TERMINAL_SOCKET` points at `/run/user/0/herdr-core/default.sock`. So a +session can tell a client exactly where to push a file instead of the client +having to guess which terminal is in front of the user. `hc` also records the +client identity per terminal (`"identity":"ghostty"`). + +`shot` reads those drops. Real copy at `/boot/config/shell/bin/shot`, installed +to `/root/.local/bin/shot` by `/boot/config/shell/install.sh` — a real +executable rather than a shell function, because a non-interactive shell (an +agent's tool call, an `ssh` command, cron) never sources `.bashrc` but does +inherit `PATH`. + +| Command | Result | +|---------|--------| +| `shot` | the newest drop for this terminal, absolute path | +| `shot -l`, `shot -n 3` | drops newest-first | +| `shot -d` | the drop folder | +| `shot -a` | drops across every terminal | + +With no drops it prints the `hc put` command that would create one. + +### Reaching the Mac from here (not yet set up) + +`hc machine ls` on this core returns `{"machines":[]}` — the relationship is +one-directional from the Mac today. Registering the Mac here would add +`hc file read|write|edit --on mac` ("an agent's hands there") and +`hc exec --on mac`, which is how a session on this box gets at Mac files +without a mount. Two caveats: `hc file read` is **text only**, so Mac images +must travel by `hc put`; and the Mac is a laptop, so register it by its tailnet +name (`100.68.118.59`, `kaloyans-macbook-air`) and expect it to be unreachable +when asleep. + +Verified 2026-11-07: the Mac answers over Tailscale in 16 ms (direct, via +`192.168.20.101`) and port 22 is open, so macOS Remote Login is already on. It +does **not** advertise Tailscale SSH (`sshHostKeys: false`; macOS cannot act as +a Tailscale SSH server), so unlike this box there is no keyless path — it needs +this host's `unraid@xtrm` key in the Mac's `authorized_keys`. A commented `Host +mac` entry is staged in `/boot/config/ssh/root/config`. + ## Notes - The binary installs to `/root/.local/bin/herdr-core`, which is not on `PATH` diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 18f8aab..0913df8 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,6 +5,17 @@ --- ## 2026-11-07 +### Herdr: self-heal scheduled, file drops wired up +- **[FIXED]** `/boot/config/herdr/herdr-selfheal.sh` **was never scheduled.** It was written on 2026-11-05 after the `hc` binary disappeared with no reboot at 25 days uptime — its own header records that a boot-time restore in `go` "is not sufficient on its own" — yet it had run exactly once, by hand. Now `*/10 * * * * /bin/sh /boot/config/herdr/herdr-selfheal.sh`, added to the `printf ... | crontab -` line in `/boot/config/go` because that line replaces the whole crontab at boot and an entry added any other way is wiped at the next restart. Verified by deleting `/usr/local/bin/hc`, confirming it left `PATH`, and watching the script relink it; the core and its 8 live terminals were unaffected. +- **[HOST]** Confirmed the crontab comment's claim of an "hourly git-ref update of the AMPECO backend checkout" corresponds to **no job** — not in the `printf`, not in the live crontab. It has not been running. Left out rather than invented; the command is still wanted if that job matters. +- **[SERVICE]** `hc put ` established as the transport for getting a Mac screenshot into a session on this box: it takes `machine/t7`, sends a local file to the terminal's machine and prints the destination. Round trip verified end to end. Drops land in `/root/.local/state/herdr-core//files//`, which is the RAM rootfs — fine for screenshots, not for anything kept, and not relocatable (0.1.17 honours no `XDG_*` override). +- **[SERVICE]** New `shot` helper (`/boot/config/shell/bin/shot` → `/root/.local/bin/shot`, installed by the shell installer) names those drops: newest path, list, folder, or across all terminals. A real executable, not a shell function, so non-interactive shells and cron can call it. `install.sh` now installs anything in `/boot/config/shell/bin/`. +- **[NETWORK]** Verified the Mac (`kaloyans-macbook-air`, `100.68.118.59`) answers over Tailscale in 16 ms direct via `192.168.20.101`, with port 22 open, so macOS Remote Login is already enabled. It does **not** advertise Tailscale SSH (macOS cannot be a Tailscale SSH server), so unlike this box there is no keyless path — reaching the Mac from here needs the `unraid@xtrm` key in its `authorized_keys`. A commented `Host mac` entry is staged in `/boot/config/ssh/root/config`. Not yet registered: `hc machine ls` here still returns `{"machines":[]}`. +- Detail in `docs/14-HERDR-REMOTE-MACHINES.md`. + +--- +## 2026-11-07 — shell environment + ### Host: zsh as root's login shell, and four silent failures fixed - **[HOST]** Root's login shell is now `/bin/zsh` (zsh 5.9 from the Slackware 15.0 patches tree, md5 verified, staged in `/boot/extra/` so `rc.local` reinstalls it at every boot). It applies to ssh, the console and the Unraid web terminal, which reads root's shell from the passwd file (`OpenTerminal.php`: `posix_getpwuid(0)['shell']`). bash remains the fallback; both shells source one shared env and alias file so they cannot drift. - **[HOST]** **`/boot/config/passwd` cannot set root's shell.** `/etc/rc.d/rc.S.cont` reads that file but hardcodes `/bin/bash`, discarding the shell field, so editing it does nothing. The change is made by `/boot/config/shell/install.sh`, which `/boot/config/go` calls after `rc.S.cont` has run. It installs zsh from `/boot/extra` if the binary is missing, and leaves root on bash if it genuinely cannot, rather than locking root out of ssh and the web terminal.