---
name: his
description: Home Infrastructure Specialist cheatsheet for mfritsche's home network. Invoke when you need quick reference on distcc hosts / DISTCC_HOSTS, waking data or pve1..pve4, /opt/herding/ tooling, lmcp endpoints, LXD container access, Fritz!DECT plugs, or how to reach *.fritz.box hosts. This is the lookup form — if you need a task delegated (take-over-and-do), use the `his` subagent instead.
version: 1.0.0
---

# HIS — Home Infrastructure Specialist (cheatsheet)

You are being given the home-infra runbook for Markus's network. Absorb and keep going — don't delegate, just use this as reference.

## Ground truth

Canonical host/container map: `/home/mfritsche/claude/CLAUDE.md` on **noether**. Deeper context: `/home/mfritsche/.claude/projects/-home-mfritsche-claude/memory/` (`MEMORY.md` is the index).

## distcc (most common lookup)

Two aarch64 workers. **Zeroconf broken in tesla's container — hand-wire it.**

| Worker | Where | Cores | Wake? |
|--------|-------|-------|-------|
| tesla | LXD on hertz (Arch) | host cores | always on |
| dcc1 | LXD on dcw3 (Pi 4) | Pi 4 | intermittent; zeroconf works (exception) |

Example for aarch64 kernel:
```
DISTCC_HOSTS="+zeroconf localhost/4 tesla.lxd:3632/4,lzo,cpp"
```

`+zeroconf` picks up dcc1 when dcw3 is up. Fall back to `192.168.88.57:3632` for tesla if mDNS isn't reachable.

**Client-side pump mode.** `,cpp,lzo` in `DISTCC_HOSTS` already tells workers they accept preprocessed `.i` files. Activate at the client: wrap `make -j$N` as `pump make -j$N` (needs `distcc` package, which ships `/usr/bin/pump`). The include-server preprocesses locally and ships only `.i` files — saves dragging `include/linux/*.h` over the wire per translation unit on kernel builds.

## Waking shared hardware

- **data** (Proxmox, .30): `sudo /opt/herding/bin/wake-data` on hertz — covers plug-off (BIOS AC-recovery) + plug-on (WoL). `--dry-run` / `--no-wait`.
- **pve1..pve4**: `sudo wake-pve <N>` on hertz (TR-064 WoL). IPs: pve1=.95 pve2=.96 pve3=.81 pve4=.54.
- **higgs** (portable Pi CM5): VPN-only, often offline.

### Data nightly-shutdown override

`/etc/cron.d/shutdown-data` on hertz fires at 03:00 daily. To inhibit for a long-running job:

```bash
busy_until=$(date -d 'tomorrow 12:00 +0200' +%s)
cat > /opt/herding/var/data-busy.lock <<EOF
$busy_until pid=$$ started=$(date -Iseconds) owner=<short-tag>
EOF
date -d "@$busy_until" -Iseconds   # self-check, must be readable
```

Honoured if `busy_until > now`; stale removed automatically. **Always echo `date -d @$busy_until -Iseconds` after writing** — 12-hour AM/PM and day-of-month off-by-one slip through silently otherwise (issue #1).

## Fritz!DECT plugs (4)

| Plug | Host | AIN | Blast radius |
|------|------|-----|--------------|
| Server | data | `11630 0190371` | data only |
| Fernseher | LG TV | `08761 0122336` | ⛔ don't automate (parenting choice) |
| **Himbeere** | **hertz** | `11630 0190369` | ⚠ kills home-LAN spine. Confirm. |
| **Office** | boltzmann + dcw3 | `08761 0122811` | Kills both build hosts. Confirm. |

## Fritz 2FA

**Kommune** physical DECT handset is the only device that satisfies Fritz 2FA (config export, sensitive TR-064). Softphones don't count. Don't queue 2FA-requiring ops unattended.

## `/opt/herding/` on hertz

Personal home-infra root, kept separate from LXD host / base OS:

```
/opt/herding/
├── bin/            symlink hub (absolute paths for now)
├── etc/
│   ├── fritz.conf       Fritz web-UI creds
│   ├── data.conf        data host facts
│   ├── hertz-tools.conf lmcp Bearer token
│   ├── gitea.creds      Gitea (marfrit) — $GITEA_URL $GITEA_USER $GITEA_TOKEN $GITEA_SSH
│   └── dokuwiki.creds   DokuWiki (markus_fritsche) — $DOKUWIKI_URL $DOKUWIKI_XMLRPC $DOKUWIKI_USER $DOKUWIKI_PASSWORD
├── fritz/fritz-pin      DHCP reservation via data.lua
└── power/wake-data      two-stage wake
```

`gitea.creds` and `dokuwiki.creds` are 0600 env files — `source` to get vars. Git push/pull uses SSH:2222 (`$GITEA_SSH`), the token is for REST only. DokuWiki XML-RPC needs the 'Remote' plugin enabled.

## lmcp endpoints (all `:8080/mcp`)

| Host | Auth | Notes |
|------|------|-------|
| hertz | Bearer (hertz-tools.conf) | 17-tool hub, .18-only bind |
| boltzmann | none | shell/files |
| tesla | none | shell/files |
| broglie | none | **dev-only** (QMK); not admin gateway |
| higgs | none | VPN-only |
| nc | Bearer (.godparticle) | public, auth required |
| pve1..pve4, kepler | Bearer (per-host) | `reference_pve_lmcp_tokens.md` |

Deferred-tools list is snapshotted at session start — new MCPs don't hot-reload. Restart Claude Code to pick up.

## VPN naming

Shannon dnsmasq serves bare + `.vpn` aliases for: ampere, edge30, ohm, ipad, fresnel, dirac. Reachable only via `@10.170.16.1` from LAN hosts.

## Monitoring

`/root/health-check.sh` on hertz, cron every 5 min. Watches: container state, SSL cert expiry (homeassistant/git/cloud/blog/dokuwiki/nextcloud/dynamic, 14-day threshold), disk (>85%), CPU temp (>80°C), thermostat battery (<20%). Alerts email mfritsche@reauktion.de with 1h–24h cooldowns.

### fahrenheit / pihole-FTL gotchas

Pi-hole 6.x on fahrenheit (Alpine + OpenRC + supervise-daemon) has two known traps:

1. **OpenRC restart orphans the FTL child** (issue #3). `rc-service pihole-FTL restart` reparents FTL to PID 1 instead of cycling it. Use an explicit kill cycle:
   ```sh
   ssh hertz "/snap/bin/lxc exec fahrenheit -- sh -c '\
     rc-service pihole-FTL stop; \
     pgrep -x pihole-FTL | xargs -r kill -TERM; sleep 2; \
     pgrep -x pihole-FTL | xargs -r kill -KILL; \
     rc-service pihole-FTL start'"
   ```

2. **civetweb optional-bind silently fails** (issue #2). `webserver.port = "80o,443os,..."` in `/etc/pihole/pihole.toml` makes the dashboard die quietly on a port collision. Drop the `o`/`os` modifiers (`"80,443s,[::]:80,[::]:443s"`) so a bind failure crashes loudly and the supervisor respawns.

Verify after: `ss -tlnp | grep pihole-FTL` should show 53/80/443 in v4 + v6 under one PID.

## Quickref — fleet inventory (who has which working copy)

`repo-inventory.sh` walks `/home/mfritsche` + `/root` depth-4, one line per repo:
`host  path  branch=X  head=Y  origin_cached=Z  origin=URL  upstream=URL`

Use `repo-inventory-nosudo.sh` when running inside a Proxmox CT via `pct exec` (already root, no sudo binary).

Latest snapshot + deductions (TF-A location, ~/src vs ~/projects/AMPere duplicates, uncommitted work): memory `reference_working_copies.md`. Re-run when hunting a clone or proposing cleanup.

## Filing bugs

Against a specific component → the component's Gitea repo issues.

  - `lmcp` runtime / hub broker behaviour → `git.reauktion.de/marfrit/lmcp/issues`
  - His runbook gaps, agent/skill prose → `git.reauktion.de/marfrit/claude-his-agent/issues`
  - Package build / repo publishing → `git.reauktion.de/marfrit/marfrit-packages/issues`
  - `vitruvius` dashboard (when pushed) → `git.reauktion.de/marfrit/vitruvius/issues`

`his:todos` on DokuWiki stays for decision-log entries and non-bug TODOs; bugs **do not** live there anymore. (Transitional: the "Recently closed" section still has legacy hash entries.)

## Rules of engagement

- **Never** flash Fritz!Box or run `update-initramfs` on remote Proxmox/ZFS hosts.
- **Ask before** rebooting shared hardware mid-debug loop.
- **Three strikes:** after 2 failed fixes, verify binary/wire/protocol before #3.
- **Fritz DNS** only serves *active* clients — offline hosts NXDOMAIN.
- **Kids-media priority:** doppler/mediagrab/Gerbera/Toniebox outages outrank pure-infra triage.
- **TV never auto:** Fernseher plug deliberately manual (TV-addiction prevention).
- **edge30 is never-root.** Primary phone, stock Motorola, stays that way.
- **Windows = clone, never fresh install.** VM106 is the one installed Win10; everything else clones from it.
- **No Oracle products.** No bedtime/schedule suggestions.
