- migration-plan.md: workstation-only migration of nh3-dev's dev env to the M2 Ultra Mac Pro Rack (on-subnet, sidecars stay on Linux). - nh3-dev README: drop volva from live sidecars + add retired note. volva.service/heid.service removed by infra-ops at heid's request (re-architected daemon->CC-orchestrator; volva.service was crash-looping).
7.1 KiB
nh3-dev → Mac Pro Rackmount migration plan
Status: planning — not started. Picked up later. Date framed: 2026-06-07 Audit snapshot: 2026-06-07 (the unsaved-work table goes stale fast — re-run before acting).
Locked decisions (operator, 2026-06-07)
| Fork | Decision | Consequence |
|---|---|---|
| Chipset | Apple Silicon (2023 Mac Pro Rack, M2 Ultra, ARM64) | Docker images need arm64 variants; x86-only binaries need Rosetta 2. macOS, not Linux. |
| Scope | Workstation only | Move Hat 1 (dev env). Leave Hat 2 (fleet sidecars) on the existing Linux VM. |
| Network | Racked in NH3, on the 10.100 subnet | Direct fleet reach; no WireGuard needed. Gets a new IP — 10.100.10.50 stays with the sidecars. |
The governing insight
nh3-dev currently wears two hats on one box, and only one follows you to the Mac:
- Hat 1 — your dev environment (moves to the Mac): ~40 repos under
~/development, all of~/.claude(skills, agents, auto-memory, settings — 1.8 GB),~/.ssh(incl.infra-ops_ed25519),.zshrc → dotfiles/,.gitconfig,.cargo,.config, toolchain. - Hat 2 — load-bearing fleet sidecars (STAY on Linux, untouched):
althing-forseti(file-based agent message-bus curator), the ttyd seats (ttyd-ro/ttyd-rw/ttyd-caddy), mead-hall:5173, NH3 egress SOCKS5 proxysocks5h://10.100.10.50:1080, NFS mounts to nh3-nas.- (2026-06-08:
volva.service+heid.serviceremoved — vestigial daemons from before Heid/Volva became CC-session orchestrators; not part of Hat 2. One fewer service in the split.)
- (2026-06-08:
Keeping Hat 2 on the existing VM (a) avoids porting Linux systemd/dante/Docker services to macOS launchd/Colima, and (b) un-tangles a latent bug: today a dev-box reboot kills fleet egress. After the split, the Mac reboots freely and nothing else notices. 10.100.10.50 stays bound to the sidecars.
⚠️ Phase 0 — Safety net (data-loss clock; do FIRST)
Why this matters: a "clean re-clone" on the Mac would silently drop unsaved work in ~15 repos. The correct cutover is rsync the working trees wholesale, not re-clone. Push everything pushable first as belt-and-suspenders.
Unsaved-work audit (snapshot 2026-06-07 — RE-RUN before acting)
| Category | Repos | Risk on naive re-clone |
|---|---|---|
| Local-only (no upstream branch) | bloom_music, comfy-dev, csm-expressiva, eitri-smithy, iris-friday, mead-hall |
May exist ONLY on this box → re-clone impossible. Must be copied. |
| Unpushed commits | eshpfi-management (+14), heid (+4), brokkr-smithy, bifrost, chatterbox-fast, nevermore, ratatoskr, skaldsong, task-board, vastai-app (each +1) |
Lost on re-clone. |
| Uncommitted working changes | vastai-app (21), voice-library (16), vor (7), heid/eitri-smithy (6), Worldtree/dvalin-smithy/task-board (4)… |
Never committed → lost on re-clone. |
| Stashes | skaldsong (1), Worldtree (1) |
Stashes don't travel through remotes at all. |
Re-run the audit:
cd ~/development
for d in */; do d=${d%/}; [ -d "$d/.git" ] || continue; cd "$d"
dirty=$(git status --porcelain 2>/dev/null | grep -c .)
stash=$(git stash list 2>/dev/null | wc -l | tr -d ' ')
if git rev-parse '@{u}' >/dev/null 2>&1; then
n=$(git rev-list '@{u}..HEAD' --count 2>/dev/null); [ "$n" != "0" ] && state="ahead-$n" || state=""
else state="NO-UPSTREAM"; fi
{ [ "$dirty" != "0" ] || [ "$stash" != "0" ] || [ -n "$state" ]; } && printf '%-34s dirty=%-3s stash=%-2s %s\n' "$d" "$dirty" "$stash" "$state"
cd ..; done
Phase 0 actions
- Push every repo with a remote + unpushed commits.
- For the 6 "no-upstream" repos: confirm whether a remote exists at all (
git remote -v). Truly local-only → copy is the ONLY path; flag them. - Do NOT auto-commit dirty trees —
rsynccarries them verbatim; pushing is just the redundant backup. - Confirm
nh3-devwill keep running headless after cutover (Hat 2 lives on).
Phase 1 — Provision the Mac (Homebrew baseline)
- Xcode Command Line Tools + Homebrew.
brew install:uv,node,rustup,git,gh,tea,zellij,ripgrep,fd,jq,rsync. Install Claude Code.- zsh is already the macOS default — but prune Linux-isms from
.zshrc(apt,/etc/profile.dsourcing,PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright).
Phase 2 — Re-home data (rsync over the 10.100 link)
rsync(trees + dirty state):~/development,~/.claude(1.8 GB),~/.ssh,~/.config,~/.gitconfig,~/.cargo,dotfiles/.chmod 600 ~/.ssh/*after copy (rsync can mangle perms; macOS ssh is strict).
Phase 3 — Repath for macOS ⚠️ (the silent breaker)
- macOS home is
/Users/lkraven, not/home/lkraven. Sweep all of~/.claude+ dotfiles for/home/lkravenand rewrite →$HOME//Users/lkraven.- Already-known hits in
~/.claude/settings.json: thestatusline-command.shpath and thealthing/pluginpath. grep -rn '/home/lkraven' ~/.claude ~/dotfilesto find the rest.
- Already-known hits in
- Reinstall MCP/plugins (
task-boardfrom giteavh/task-board,althing) and re-point absolute paths. - ARM64 reality:
- Docker (if needed locally) → OrbStack/Colima + arm64 images.
- Playwright →
npx playwright install chromium(arm64 supported; no/opt/ms-playwrightshared trick needed on a single-user box). - Python wheels are almost all arm64-mac now — only flag x86-only wheels (→ Rosetta 2).
Phase 4 — Fleet citizenship for the new box
- Assign a new IP on 10.100 (NOT
10.100.10.50— that's the sidecars'). Add to~/.ssh/config, fleet DNS, andscripts/add-host.shif tracking here. - WireGuard: not needed (no WG tunnel terminates on this box; Irvine reach was never a Hat-1 concern).
- Stand up a backup for the Mac (Time Machine, or a new restic client →
rest-server-nh3). The risk table is the argument for not skipping this — real local-only state travels with you.
Small decisions left (with recommendations)
- NFS
/mnt/smithy+/mnt/backupon the Mac? → Recommend/mnt/smithyyes (R14/smithy workflows use it),/mnt/backupno. (macOS NFS works, but the Synology export must list the Mac's new IP; macOS prefers autofs over fstab.) - iPad/web seat into the Mac's dev session? → Recommend defer. (ttyd seats stay pointed at the Linux zellij session; a Mac seat is a fresh launchd port — only if you still drive from iPad.)
- Hostname → Avoid
nh3-dev2confusion; pick role-clear, e.g.nh3-mac/nh3-workbench.
/elitk takeaway
Treat this as "set up a new workbench and copy my stuff over the network," not "move the server." Push everything first, then copy the folders wholesale (don't re-clone — you'd lose unsaved work in ~15 repos), fix the /home/ → /Users/ paths, and leave the little fleet-utility box humming untouched in the rack.
Next action when resumed: run Phase 0 (push pushable repos; confirm which of the 6 no-upstream repos are truly local-only). It's the only step with a data-loss clock; everything else waits on the hardware.