Home / Ops / Deploy and Infrastructure
Updated Jun 11, 2026 · Affirmology_MacMiniSetup_Guide_v1.md
Prepared for Jeff Parker Goal Get your Mac mini running as the always-on Affirmology backend server. You interact with it primarily from your laptop through Claude Desktop. Files exchange seamlessly between the two machines via Tailscale + SSHFS. The nightly corpus build runs on the Mac mini; the laptop is your control surface.
Approximate time 90 minutes if everything cooperates. Done once, never again.
| Where | Role |
|---|---|
| Mac mini (always on, plugged in) | Runs the corpus scraper nightly. Hosts the SSD with all the data. Runs Claude Code for direct backend tinkering. |
| Laptop (your daily driver) | Claude Desktop, code editing, monitoring. Reads/writes the Mac mini's files through a mounted folder that looks like a local drive. |
SSD (Affirmology) |
Lives on the Mac mini, holds the corpus database and raw scraped text. The Mac mini accesses it directly; the laptop accesses it through the SSH mount. |
| Tailscale | Private, encrypted network between your laptop and Mac mini. Survives Wi-Fi changes, hotel networks, traveling. No port forwarding, no DDNS. |
You don't need a wired keyboard, wired mouse, or CD drive. None of those have shipped with a Mac mini in over a decade. The M-series Mac mini's Setup Assistant pairs Bluetooth devices during the welcome screens.
What you need on hand: - HDMI monitor (your switcher is fine) - Power cable for the Mac mini - One wireless keyboard or wireless mouse with Bluetooth (NOT a USB receiver dongle - has to be real Bluetooth). Apple Magic Mouse/Keyboard, Logitech MX, Keychron, etc. all qualify. - Wi-Fi password handy
What to do: 1. Plug the Mac mini into power and HDMI. Don't power it on yet. 2. Put your wireless device in pairing mode (usually: hold the power button for 3 seconds until the LED blinks). 3. Power on the Mac mini. 4. The Apple logo appears, then "Hi" → welcome screen. The Setup Assistant will display a message asking you to pair a Bluetooth device. Your wireless mouse/keyboard should show up in the list. Select it, click "Pair." Once paired, navigation works. 5. The rest of Phase 1 below proceeds normally.
If you somehow get stuck mid-setup with no paired input device: - Power off the Mac mini (hold power button 10 seconds) - Power back on with a different wireless device in pairing mode - Setup Assistant re-tries the pairing prompt
You will NOT need a CD drive. macOS hasn't installed from CD since 2009. Everything's over the internet via Recovery Mode if anything goes wrong (Cmd+R at boot).
If you really want a wired-input backup option: USB-C to USB-A adapter ($8 at any electronics store) plus your old laptop's USB keyboard/mouse if you have one. Not required.
affirmology-mini (the rest of this guide assumes this exact name).
- Skip Touch ID and Siri if you want - neither is needed.jeffreyparker@affirmology-mini.local. You'll use this from the laptop in Phase 2.Open Terminal on the Mac mini. Paste:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Follow the prompts (it asks for your password, takes ~5 minutes). When it finishes, it prints two lines about adding Homebrew to your shell - paste those into Terminal too.
Then install everything we need:
brew install python@3.11 git ffmpeg sqlite gh uv
brew install --cask iterm2 visual-studio-code
brew install --cask tailscale
brew install --cask google-cloud-sdk
brew install --cask claude
brew install sshfs # for the laptop-mounts-Mac-mini direction
The claude cask installs Claude Code on the Mac mini.
mkdir -p ~/CLAUDE/AFFIRMOLOGY
cd ~/CLAUDE/AFFIRMOLOGY
Now you have two options:
Option A (preferred): if the code is already in GitHub:
git clone https://github.com/YOUR-USERNAME/affirmology-agent.git
Option B (works today, less ideal long-term): copy over the laptop's folder via the SSH-mount in Phase 2, then optionally push to GitHub later.
Either way, you should end up with ~/CLAUDE/AFFIRMOLOGY/affirmology-agent/ populated.
cd ~/CLAUDE/AFFIRMOLOGY/affirmology-agent
python3.11 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -e ".[dev]"
pip install trafilatura tenacity pypdf google-generativeai google-cloud-aiplatform youtube-transcript-api yt-dlp httpx weasyprint
This installs all the Affirmology dependencies plus the corpus build dependencies.
100.x.y.z and a hostname like affirmology-mini.tailfee.ts.net.System Settings → Battery (or Energy Saver): - "Prevent automatic sleeping when the display is off" → ON - "Wake for network access" → ON - "Start up automatically after a power failure" → ON
System Settings → General → Software Update → Automatic Updates → click ⓘ: - Turn OFF "Install macOS Updates" (auto-restarts kill long jobs) - Leave the others on if you want, off if you want maximum stability
Create the launchd plist that runs the scraper every night between 11pm and 9am:
mkdir -p ~/Library/LaunchAgents
cat > ~/Library/LaunchAgents/com.affirmology.nightly.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.affirmology.nightly</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>cd ~/CLAUDE/AFFIRMOLOGY/affirmology-agent && source .venv/bin/activate && caffeinate -i -m -s env PYTHONPATH=src python -m affirmology.corpus.run --data-dir /Volumes/Affirmology/corpus --traditions all --mode scrape --max-sources-per-tradition 200 --max-priority 5 --per-source-timeout-seconds 3600 --stop-after-seconds 36000 --max-cost-usd 25 > /Volumes/Affirmology/corpus/logs/nightly_$(date +%Y-%m-%d).log 2>&1</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key><integer>23</integer>
<key>Minute</key><integer>0</integer>
</dict>
<key>RunAtLoad</key><false/>
<key>StandardOutPath</key><string>/Volumes/Affirmology/corpus/logs/launchd-stdout.log</string>
<key>StandardErrorPath</key><string>/Volumes/Affirmology/corpus/logs/launchd-stderr.log</string>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/com.affirmology.nightly.plist
launchctl list | grep affirmology
The last command should print one line confirming the job is registered. It will fire every night at 11pm and run for up to 10 hours.
brew install --cask tailscale
Open the Tailscale app, sign in with the same account. Both machines should now appear in the Tailscale admin console (https://login.tailscale.com/admin/machines) with green dots next to them.
ssh jeffreyparker@affirmology-mini
(The .local suffix isn't needed when both machines are on the same Tailscale network - Tailscale resolves the hostname automatically.)
Type yes on the first connection to accept the host key. Enter your Mac mini password. You should land in the Mac mini's shell. Confirm:
hostname
exit
hostname should print affirmology-mini. Then exit brings you back to your laptop.
On your laptop:
# Generate a key if you don't have one
[ -f ~/.ssh/id_ed25519 ] || ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519
ssh-copy-id jeffreyparker@affirmology-mini
Now ssh jeffreyparker@affirmology-mini logs in without a password.
The cleanest way to make the Mac mini's files appear as a local drive on your laptop. Skip this if you'd rather copy files over manually via scp (works fine, just less convenient).
brew install --cask macfuse
brew install gromgit/fuse/sshfs-mac
After install, restart your laptop once. macFUSE requires a kernel extension that needs a reboot to activate.
mkdir -p ~/affirmology-mini-mount
Then mount the Mac mini's home folder so it appears under that path on your laptop:
sshfs jeffreyparker@affirmology-mini:/Users/jeffreyparker ~/affirmology-mini-mount -o defer_permissions,reconnect,follow_symlinks,allow_other
Now ~/affirmology-mini-mount/ on your laptop is the Mac mini's home directory. You can browse it in Finder, open files in any editor, drag files in and out, everything.
To unmount cleanly when you're done:
umount ~/affirmology-mini-mount
Create a launchd plist on your laptop:
cat > ~/Library/LaunchAgents/com.affirmology.mount.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.affirmology.mount</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>/usr/local/bin/sshfs jeffreyparker@affirmology-mini:/Users/jeffreyparker /Users/jeffreyparker/affirmology-mini-mount -o defer_permissions,reconnect,follow_symlinks,allow_other</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/com.affirmology.mount.plist
Now every time you boot the laptop and Tailscale connects, the Mac mini's files appear at ~/affirmology-mini-mount/ automatically.
You can open the mounted folder in Claude Desktop just like any local folder. Cowork sees ~/affirmology-mini-mount/CLAUDE/AFFIRMOLOGY/affirmology-agent/ as a regular local path. You can read, write, and run any file tools against the Mac mini's content without leaving your laptop.
When you ask Claude to run something on the Mac mini, the cleanest pattern is:
ssh jeffreyparker@affirmology-miniexit when doneOr, for a one-off command:
ssh jeffreyparker@affirmology-mini "cd ~/CLAUDE/AFFIRMOLOGY/affirmology-agent && source .venv/bin/activate && PYTHONPATH=src python -m affirmology.corpus.status --data-dir /Volumes/Affirmology/corpus"
That runs the status check on the Mac mini and prints the result on your laptop, all without leaving your laptop's terminal.
From any terminal anywhere (your phone with Termius, a laptop in a hotel), as long as Tailscale is connected:
ssh jeffreyparker@affirmology-mini "cat /Volumes/Affirmology/corpus/logs/heartbeat.json"
Shows what the nightly scraper is doing right now.
Install the Remote - SSH extension in VS Code. Then:
jeffreyparker@affirmology-miniThis is the most powerful pattern for actual development. Files live on the Mac mini, editing happens on the laptop, code runs on the Mac mini.
When you edit a file on your laptop and want it on the Mac mini quickly (without going through Git):
rsync -avz --delete \
~/CLAUDE/AFFIRMOLOGY/affirmology-agent/ \
jeffreyparker@affirmology-mini:~/CLAUDE/AFFIRMOLOGY/affirmology-agent/
That's a one-way push. For two-way, use Git as the source of truth and git pull on both machines.
Each morning when you sit down with coffee:
ssh jeffreyparker@affirmology-mini "cd ~/CLAUDE/AFFIRMOLOGY/affirmology-agent && source .venv/bin/activate && PYTHONPATH=src python -m affirmology.corpus.status --data-dir /Volumes/Affirmology/corpus"
That tells you what the night produced: how many new documents, how many new structured records, total spend, recent errors.
If anything went sideways:
# Kill any still-running scraper
ssh jeffreyparker@affirmology-mini "pkill -f affirmology.corpus.run"
# Check the most recent log
ssh jeffreyparker@affirmology-mini "ls -t /Volumes/Affirmology/corpus/logs/ | head -3"
# Tail the most recent log
ssh jeffreyparker@affirmology-mini "tail -100 /Volumes/Affirmology/corpus/logs/nightly_$(date +%Y-%m-%d).log"
Pause or unschedule the nightly job if you need to:
ssh jeffreyparker@affirmology-mini "launchctl unload ~/Library/LaunchAgents/com.affirmology.nightly.plist"
# Re-enable later:
ssh jeffreyparker@affirmology-mini "launchctl load ~/Library/LaunchAgents/com.affirmology.nightly.plist"
"sshfs: command not found" after install. macFUSE needs a reboot to activate the kernel extension. Restart the laptop and try again.
"ssh: Could not resolve hostname affirmology-mini". Tailscale isn't connected on one of the machines. Check the Tailscale menu bar icon on both - both should be green.
"Permission denied (publickey)". SSH key auth not set up. Re-run ssh-copy-id jeffreyparker@affirmology-mini from the laptop.
Mount disappears randomly. SSHFS sometimes loses connection. The reconnect flag in the mount command helps; if it still happens, umount ~/affirmology-mini-mount and re-mount.
The nightly job doesn't fire. Check launchctl list | grep affirmology shows the job. If it's missing, the plist didn't load - re-run the load command. Also check ~/Library/LaunchAgents/com.affirmology.nightly.plist actually exists and has correct XML (no trailing whitespace issues from copy-paste).
When something breaks: SSH in, fix it, exit. When something works: it keeps working without your attention.
End of guide. If anything in here doesn't apply to your exact setup, tell me and I'll write a corrected version.