docs: Unraid as a Herdr machine, and why it needed a --via wrapper

herdr-core machine add kept failing with "core did not start within 5 s".
Cause: hc puts its core socket under XDG_RUNTIME_DIR, which on Unraid is
/run/user/0 — created and destroyed per login session by elogind. The socket
vanished with the session that started the core, so later clients found an
empty directory. The core itself kept running on an unlinked socket, and
core.log stayed empty because the failure is client side.

Fixed entirely on the Mac with a --via transport that pins
XDG_RUNTIME_DIR=/var/run/herdr-rt. Nothing on the server was changed. The
transport uses key auth on 192.168.10.20:422, not Tailscale SSH, since --via
needs something that asks nothing and the default tailnet ACL demands
periodic browser re-auth.

Also records the reboot caveat (/ is RAM, so the remote hc binary is lost)
with a go-script fix written up but not applied, and corrects the
192.168.31.0/24 note: that shim was created for a Pi-hole that AdGuard
replaced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kaloyan DanchevandClaude Opus 5 committed 2026-11-03 22:29:40 +02:00
1 parent da981a7b40
commit 32a60f0c6e
3 files changed
+106 -1

No files matched your search

+99
View File
@@ -0,0 +1,99 @@
# Herdr (hc) Remote Machines — Unraid
Herdr (`Herdr.app`, bundle `dev.herdr.app`, CLI `herdr-core` aka `hc`) runs
programs and coding agents in terminals that stay alive, including on remote
machines reached over SSH. The Unraid server is registered as the machine
`unraid`.
Added 2026-11-03.
## Registration
The transport is a wrapper on the Mac, not a plain SSH target:
```bash
herdr-core machine add --via ~/.local/bin/herdr-via-unraid --name unraid --install
```
```
unraid | state: connected | host: xtrm-unraid | hc: 0.1.17
```
Verify with `herdr-core exec --on unraid 'hostname'` → `XTRM-Unraid`.
## Why a wrapper and not `machine add root@192.168.10.20:422`
`hc` starts a core process (`herdr-core serve`) on the remote and talks to it
over a unix socket under `XDG_RUNTIME_DIR`. On Unraid that resolves to
`/run/user/0`, which **elogind creates and destroys per login session**. So:
1. `machine add` opens an SSH session and starts the core. The socket lands in
`/run/user/0/herdr-core/default.sock`.
2. The session ends. elogind removes `/run/user/0`, taking the socket with it.
3. The next client connects in a new session, finds a fresh empty
`/run/user/0`, and fails with `core did not start within 5 s`.
The core is not at fault — it keeps running, holding a socket that has been
unlinked from the filesystem. `core.log` stays 0 bytes because the failure is
client side. Unraid also warns `hc: no systemd user manager here: the core ends
with whatever started it`, which is why `hc` cannot supervise its own core here.
The fix is to pin `XDG_RUNTIME_DIR` to a path that outlives a login session.
Doing it in the `--via` transport keeps the change on the Mac and leaves the
server's shell configuration untouched.
`~/.local/bin/herdr-via-unraid`:
```sh
#!/bin/sh
# Herdr appends the shell line to run as the last argument.
exec ssh -T -o BatchMode=yes -o ConnectTimeout=10 \
-p 422 -i "$HOME/.ssh/id_ed25519_unraid" root@192.168.10.20 \
"export XDG_RUNTIME_DIR=/var/run/herdr-rt; mkdir -p \"\$XDG_RUNTIME_DIR\" && chmod 700 \"\$XDG_RUNTIME_DIR\"; $*"
```
## Why the LAN address and not the Tailscale IP
`--via` requires a transport that passes bytes untouched and **asks nothing**.
Tailscale SSH on `100.100.208.69` sits behind the default tailnet ACL
`action: "check"`, which demands browser re-authentication each period — fine
interactively, fatal for a long-lived Herdr connection.
Key auth on `192.168.10.20:422` asks nothing, and from outside the LAN it still
travels over Tailscale via the MikroTik subnet router (see
`docs/09-TAILSCALE-VPN.md`). Changing that ACL rule to `accept` for
`autogroup:self` would make the `100.x` address usable here too.
## Reboot persistence
Unraid runs `/` from `rootfs` (RAM) and `/run` as tmpfs, so both of these are
lost on every reboot:
| Path | Lost on reboot | Recovery |
|------|----------------|----------|
| `/var/run/herdr-rt` | yes | self-healing — the `--via` wrapper recreates it on every connection |
| `/root/.local/bin/herdr-core` | yes | **manual** — `herdr-core machine update`, or `machine add --install` again |
To make the binary survive, stash it on the boot flash and restore it from the
`go` script, in the same style as the existing `install`/`cp` lines there:
```bash
mkdir -p /boot/config/herdr
cp /root/.local/bin/herdr-core /boot/config/herdr/herdr-core
```
then append to `/boot/config/go`:
```bash
# Herdr (hc) remote machines. Unraid runs / from RAM, so the hc binary installed
# by `herdr-core machine add --install` is lost on reboot. Restore it from /boot.
# The runtime dir is not restored here: the Mac-side --via wrapper exports
# XDG_RUNTIME_DIR=/var/run/herdr-rt and creates it on every connection, because
# the default /run/user/0 is destroyed per login session by elogind.
mkdir -p /root/.local/bin
install -m 755 /boot/config/herdr/herdr-core /root/.local/bin/herdr-core 2>/dev/null
```
`install -m 755` matters: `/boot` is FAT32 and does not carry the execute bit.
**Status: not applied.** Pending.