Files
infrastructure/docs/14-HERDR-REMOTE-MACHINES.md
T
Kaloyan DanchevandClaude Opus 5 1613f7f458 docs: Herdr on Tailscale SSH, with linger as the real socket fix
Rewrites the Herdr doc to match what was actually built. The machine now
targets root@100.100.208.69 over Tailscale SSH instead of the LAN address,
so it no longer depends on the mikrotik-tailscale subnet router.

The "core did not start within 5 s" failure was the hc core socket living
under XDG_RUNTIME_DIR = /run/user/0, which pam_elogind creates and destroys
per login session. loginctl enable-linger root keeps that directory alive, so
every client agrees on the default socket path. The earlier Mac-side env pin
fixed the CLI but broke the GUI's plain-ssh "Add a machine"; linger
supersedes it and the wrapper is kept but unused.

Also records the stale ~/.ssh/config entry that pointed unraid at the deleted
100.100.208.70 on port 422, which is what the GUI had been listing, and the
go-script block that restores both the binary and linger at boot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-11-03 23:37:46 +02:00

5.9 KiB

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.

Set up 2026-11-03.

Current configuration

herdr-core machine add root@100.100.208.69 --name unraid --install
unraid | state: connected | target: root@100.100.208.69 | hc: 0.1.17

Verify with herdr-core exec --on unraid 'hostname' → XTRM-Unraid.

The target is Unraid's own Tailscale node over Tailscale SSH (port 22), not the LAN address. Consequences:

  • It does not depend on the mikrotik-tailscale subnet router, which has failed before (see the 2026-08-10 recovery note in docs/09-TAILSCALE-VPN.md). Only Unraid itself and the tailnet need to be healthy.
  • It works identically at home and away, with no configuration to change. Tailscale still picks the efficient path — on the LAN it reports direct 192.168.10.20:41725 rather than relaying.
  • Authentication is tailnet identity, not an SSH key. Access therefore rests on the tailnet SSH policy, which was set to accept on 2026-11-03. Under the previous check policy this would have demanded a browser re-auth every ~12 h and stalled.

Two problems had to be solved first

1. The socket path — core did not start within 5 s

hc runs a core process (herdr-core serve) on the remote and talks to it over a unix socket under XDG_RUNTIME_DIR. On Unraid that is /run/user/0, which pam_elogind.so 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 with the socket inside it.
  3. The next client connects in a new session, finds a fresh empty /run/user/0, and times out after 5 s.

The core is not at fault — it keeps running on a socket that has been unlinked. core.log stays 0 bytes because the failure is client side. Unraid also prints hc: no systemd user manager here: the core ends with whatever started it, which is why hc cannot supervise its own core here.

Fix: loginctl enable-linger root. Unraid ships loginctl even without systemd, and linger keeps /run/user/0 alive across logouts, so the default socket path is stable and every client agrees on it.

An earlier attempt pinned XDG_RUNTIME_DIR=/var/run/herdr-rt from a Mac-side --via wrapper. That worked for the CLI but broke the GUI's "Add a machine", which uses plain ssh and so looked in /run/user/0:

the core listens at /var/run/herdr-rt/herdr-core/default.sock,
but this shell looks at /run/user/0/herdr-core/default.sock
hint: set HC_SOCKET to the first path

Linger supersedes that. The wrapper is retained at ~/.local/bin/herdr-via-unraid (LAN primary, Tailscale fallback, chosen by a TCP probe before any ssh runs so a failed attempt cannot consume stdin) but is not in use. To go back to it: herdr-core machine rm unraid, then machine add --via ~/.local/bin/herdr-via-unraid --name unraid.

2. A stale ~/.ssh/config entry

The GUI lists candidates from ~/.ssh/config, which still held the Tailscale IP of the node deleted on 2026-11-03 — and pointed it at a port that was never open there:

Host 100.100.208.70 unraid     # node no longer exists
  HostName 100.100.208.70
  Port 422                      # sshd binds only 192.168.10.20 and 192.168.31.250

Replaced with two correct entries (backup at ~/.ssh/config.bak-20261103):

Host unraid                     # LAN address; routes over Tailscale when away
  HostName 192.168.10.20
  User root
  Port 422
  IdentityFile ~/.ssh/id_ed25519_unraid

Host unraid-ts 100.100.208.69   # Tailscale SSH: port 22, no key
  HostName 100.100.208.69
  User root

Reboot persistence

Unraid runs / from rootfs (RAM) and /run as tmpfs, so all of this is lost on reboot and is restored by /boot/config/go:

Path Lost on reboot Restored by
/root/.local/bin/herdr-core yes install -m 755 from /boot/config/herdr/herdr-core
/var/lib/elogind/linger/root yes loginctl enable-linger root
/run/user/0 yes recreated by pam_elogind at first login, then held open by linger

The go block:

# 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.
mkdir -p /root/.local/bin
install -m 755 /boot/config/herdr/herdr-core /root/.local/bin/herdr-core 2>/dev/null

# Keep /run/user/0 alive across logouts. pam_elogind creates and destroys it per
# login session, and the hc core's socket lives there, so without linger the
# socket disappears with the session that started the core and every later
# client fails with "core did not start within 5 s". The linger marker is
# /var/lib/elogind/linger/root, and /var/lib is RAM here, so it must be re-set
# at every boot.
loginctl enable-linger root 2>/dev/null

install -m 755 matters: /boot is FAT32 and carries no execute bit.

Applied 2026-11-03, sh -n /boot/config/go parses cleanly. Backups of the go 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.

Notes

  • ~/.local/bin is not on PATH in a non-interactive shell on Unraid, so herdr-core resolves only by absolute path there. Herdr itself always uses the absolute path, so this does not affect normal use.
  • Adding a machine that hc already knows is refused as a duplicate — it keys on the core id (3d4a2397fcb6…), not the address. Re-pointing an existing machine means machine rm then machine add, since there is no command to change a target in place.