diff --git a/docs/09-TAILSCALE-VPN.md b/docs/09-TAILSCALE-VPN.md index 2923ef0..982547d 100644 --- a/docs/09-TAILSCALE-VPN.md +++ b/docs/09-TAILSCALE-VPN.md @@ -228,7 +228,7 @@ logged out since roughly May 2026 and was re-enrolled on 2026-11-03. | Pref | Value | Reason | |------|-------|--------| -| `--advertise-routes` | none | The old `192.168.31.0/24` advertisement was dropped. That subnet is only the macvlan shim (`br0-shim` holds `192.168.31.250/32` with one host route to `192.168.31.4`) — nothing reachable lives there. | +| `--advertise-routes` | none | The old `192.168.31.0/24` advertisement was dropped. That subnet is only the macvlan shim created in `/boot/config/go` for a Pi-hole at `192.168.31.4`. Pi-hole was replaced by AdGuard: there is no such container any more and the address is silent, so nothing reachable lives there. | | LAN subnet routing | not advertised | The MikroTik container is primary for `192.168.10.0/24`, `.20`, `.25`, `.30`. A second advertiser for a subnet Unraid itself sits on would create a routing conflict. | | `--advertise-exit-node` | on | Redundant with the hAP today, kept as a fallback on the 24/7 box. **Approved in the admin console 2026-11-03** — shows as `offers exit node` and appears in `tailscale exit-node list`. | | `--accept-routes` | **false** | Critical. Unraid is *on* `192.168.10.0/24`; accepting the hAP's route for its own subnet would loop. This is why `tailscale status` reports the health warning "Some peers are advertising routes but --accept-routes is false" — expected here, not a fault. | diff --git a/docs/14-HERDR-REMOTE-MACHINES.md b/docs/14-HERDR-REMOTE-MACHINES.md new file mode 100644 index 0000000..0669722 --- /dev/null +++ b/docs/14-HERDR-REMOTE-MACHINES.md @@ -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. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7d5dc7e..05ccee1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,12 @@ - **[SERVICE]** Exit node advertised from Unraid as a fallback to the hAP, and approved in the Tailscale admin console the same day. The tailnet now has two exit nodes: `mikrotik-tailscale` (100.74.219.35, primary) and `xtrm-unraid` (100.100.208.69, fallback). Neither has been exercised in anger from the Mac — without `--exit-node-allow-lan-access` an exit node cuts the client's own LAN access. - Detail, including the root-only `AllowTcpForwarding` behaviour in Unraid's stock sshd config, in `docs/09-TAILSCALE-VPN.md`. +### Tooling: Unraid registered as a Herdr (hc) machine +- **[SERVICE]** Unraid added to Herdr as the machine `unraid` (`hc` 0.1.17 on the remote). `herdr-core machine add` had been failing with `core did not start within 5 s`: `hc` puts its core socket under `XDG_RUNTIME_DIR`, which on Unraid is `/run/user/0` — a directory elogind creates and destroys per login session, so the socket disappeared the moment the session that started the core ended. The core kept running with an unlinked socket, and `core.log` stayed empty because the failure is client side. +- **[SERVICE]** Fixed from the Mac only, via `~/.local/bin/herdr-via-unraid`, a `--via` transport that exports `XDG_RUNTIME_DIR=/var/run/herdr-rt` on every connection. Nothing on the server was changed. Uses key auth on 192.168.10.20:422 rather than Tailscale SSH, because `--via` needs a transport that asks nothing and the default tailnet ACL `check` would demand periodic browser re-auth. +- **[SERVICE]** Reboot caveat: `/` is rootfs (RAM) and `/run` is tmpfs on Unraid, so `/root/.local/bin/herdr-core` is lost at every boot and needs `herdr-core machine update` to restore. A `/boot/config/go` entry to restore it automatically is written up but **not applied**. Detail in `docs/14-HERDR-REMOTE-MACHINES.md`. +- Corrected the 192.168.31.0/24 note: the shim was created in the go script for a Pi-hole at 192.168.31.4, which AdGuard replaced. No such container exists and the address is silent. + --- ## 2026-10-16 - Home Assistant: `corridor-bt-proxy` (Olimex ESP32-POE-ISO) added to the ESPHome integration and running as an active Bluetooth proxy. Temporarily on hAP ax3 ether3, VLAN 10 (192.168.10.100); it moves to CAP XL ether2 (VLAN 30) when mounted.