Bootstrap the Spark
Everything here runs on the Spark, as you, in an SSH session from the Mac, except where a step says the Mac or agent.
Before you start
On the Mac, SSH into the Spark over Wi-Fi (its
.200address) — not the wired interface. Bouncing the interface you’re connected through would drop your own session.On the Spark, find the wired interface’s name:
nmcli device status.On the Spark, bounce it so it takes its DHCP reservation:
sudo nmcli device disconnect <wired-iface> && sudo nmcli device connect <wired-iface>On the Spark, confirm the wired address now ends in
.201.
Run it
On the Spark, as you, from the clone (cd ~/git/hub/local-ai), in an SSH session. Type make bootstrap, never sudo make bootstrap: the target asks for your password itself, and under an outer sudo it would give your groups to root.
make bootstrap-dry-run— read the output before doing anything else. Its hold step printsGPU set: N packages, M already held: on a bootstrapped Spark, all of them held (151 at the 2026-09-24 bootstrap). Off the Spark (the Mac, CI), with no DGX kernel installed, it saysa real run stops hereinstead. A package that isn’t cleanly installed stops the dry run too, with a hint for finishing it.make bootstrap— run it over SSH; it stops any running desktop session.
After bootstrap
- Log out and back in — new group membership only takes effect on a fresh login.
systemctl get-default→multi-user.targetsystemctl is-active earlyoom→activesudo ufw status→ OpenSSH allowedid agent→ nodocker,sudoorspark-adminin the listapt-mark showhold | grep -E '^(linux-image-nvidia-hwe|nvidia-driver|cuda-toolkit)-'→ the kernel, the driver and CUDA among the held GPU set. From here on, updates follow Updates.free -g→ record this as the new baseline
Agent’s SSH and Claude Code login
On the Mac, make agent its own key and copy the public half to the Spark. Keys and the Mac’s
~/.ssh/configare in SSH from the Mac:ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519-brightroar_agent -C "agent@brightroar" scp ~/.ssh/id_ed25519-brightroar_agent.pub brightroar:agent-key.pubOn the Spark, install it for
agent, from an interactivessh brightroarsession.sudoneeds a terminal to ask for your password, and a command piped intosshdoesn’t have one:sudo -u agent sh -c 'umask 077 && mkdir -p /home/agent/.ssh && cat >> /home/agent/.ssh/authorized_keys' < ~/agent-key.pub && rm ~/agent-key.pubYour shell opens the key file and hands it over on standard input, and
agentwrites it into its own home:umask 077makes.ssh0700 andauthorized_keys0600 if they are new. Root never writes,chowns orchmods anything under/home/agent.agentcontrols that folder, so a link it planted there could turn a root write into a write to any file on the box. The copy is deleted only once the key is in. Run again, it stops at the missing copy and changes nothing; with a fresh copy, it adds the key a second time, which is harmless.On the Mac, log in as
agent:ssh brightroar-agent(the alias from SSH from the Mac)On the Spark, as
agent— the installer refuses to run under sudo:curl -fsSL https://claude.ai/install.sh | bashOn the Spark, still as
agent, log in: runclaude. With no browser on the box, presscto copy the login URL, open it on the Mac, and paste the code back intoagent’sclaudeon the Spark. (Added 2026-09-28: its secrets guard comes in pi, beforeagentgets a key.)
agent makes its own ~/work too, the same way: its first git clone into it creates it. Bootstrap doesn’t, since it runs as root.
Live checks (you run these — they need sudo)
On the Spark, as you — not in step 3’s agent session, which has no sudo:
sudo -u agent sh -c 'if test -x "$1"; then echo "OPEN: stop and fix permissions"; else echo "closed: good"; fi' _ "$HOME"→closed: good.$HOMEexpands in your shell, so it names your home whatever your login is;test -xasks whetheragentcan enter it at all, without reading anything in it. The verdict comes fromagent’s own shell, so a failedsudoshows its error, never a false “good”. Plain-u, not-iu:-ire-reads the command inagent’s login shell, which would expand$1there, to nothing.sudo -iu agent docker ps→ permission deniedsudo -iu agent nvidia-smi --query-gpu=name --format=csv,noheader→ the GPU’s name, without the per-unit UUID thatnvidia-smi -Lprints
Re-run once
make bootstrap again → finishes with no errors, and no new users, groups or config lines.