SPB Git

spb/autoscholar

Public
8.1 KB · 110 lines shellscript
1#!/bin/bash2# AutoScholar worker installer for a MacLustr node (macOS Apple Silicon). Run by the operator with sudo ONCE:3#   sudo bash install-macos.sh --control-plane http://192.168.2.85:8470 --name M4M64c --enrollment-secret <secret> --anthropic-key-file /path/to/keyfile4# Creates the dedicated non-admin user `asworker`, /var/autoscholar (owned by asworker), /etc/autoscholar/worker.env (root:asworker 0640),5# installs toolchain (tectonic, poppler, uv) via Homebrew *as the operator* (brew refuses root), deploys the worker bundle,6# loads the LaunchDaemon io.autoscholar.worker (UserName=asworker) and a pf anchor blocking LAN egress for asworker except the control plane.7set -euo pipefail8CONTROL_PLANE=""; NAME="$(scutil --get ComputerName 2>/dev/null || hostname)"; SECRET=""; KEYFILE=""; BUNDLE_DIR="$(cd "$(dirname "$0")/.." && pwd)"; OPERATOR="${SUDO_USER:-$(whoami)}"9while [ $# -gt 0 ]; do case "$1" in10  --control-plane) CONTROL_PLANE="$2"; shift 2;;11  --name) NAME="$2"; shift 2;;12  --enrollment-secret) SECRET="$2"; shift 2;;13  --anthropic-key-file) KEYFILE="$2"; shift 2;;14  --bundle) BUNDLE_DIR="$2"; shift 2;;15  *) echo "unknown arg $1"; exit 2;; esac; done16[ "$(id -u)" = 0 ] || { echo "run with sudo"; exit 1; }17[ -n "$CONTROL_PLANE" ] || { echo "--control-plane required"; exit 1; }18BREW=/opt/homebrew/bin/brew19echo "== 1. dedicated user asworker"20if ! dscl . -read /Users/asworker >/dev/null 2>&1; then21  sysadminctl -addUser asworker -fullName "AutoScholar Worker" -password "$(openssl rand -base64 24)" -home /var/autoscholar/home -shell /bin/zsh >/dev/null 2>&1 || true22  dscl . -create /Users/asworker IsHidden 123fi24dseditgroup -o edit -d asworker -t user admin 2>/dev/null || true   # never admin25dseditgroup -o read asworker >/dev/null 2>&1 || dseditgroup -o create -r "AutoScholar Worker" asworker26dseditgroup -o edit -a asworker -t user asworker 2>/dev/null || true27id asworker28echo "== 2. directories"29mkdir -p /var/autoscholar/{runs,state,cache/tectonic,home,templates/latex,logs,bundle}30chown -R asworker:asworker /var/autoscholar31chmod 750 /var/autoscholar32chmod 700 /var/autoscholar/state /var/autoscholar/home33mkdir -p /etc/autoscholar34echo "== 3. toolchain via Homebrew (as $OPERATOR)"35sudo -u "$OPERATOR" $BREW list tectonic >/dev/null 2>&1 || sudo -u "$OPERATOR" $BREW install tectonic36sudo -u "$OPERATOR" $BREW list poppler >/dev/null 2>&1 || sudo -u "$OPERATOR" $BREW install poppler37sudo -u "$OPERATOR" $BREW list uv >/dev/null 2>&1 || sudo -u "$OPERATOR" $BREW install uv38sudo -u "$OPERATOR" $BREW list python@3.13 >/dev/null 2>&1 || sudo -u "$OPERATOR" $BREW install python@3.1339echo "== 4. worker bundle → /var/autoscholar/bundle"40rsync -a --delete --exclude ".git" "$BUNDLE_DIR/" /var/autoscholar/bundle/41cp "$BUNDLE_DIR"/../../research/templates/latex/* /var/autoscholar/templates/latex/ 2>/dev/null || cp /var/autoscholar/bundle/templates/latex/* /var/autoscholar/templates/latex/ 2>/dev/null || true42chown -R asworker:asworker /var/autoscholar/bundle /var/autoscholar/templates43echo "== 5. environment file (root:asworker 0640; secrets never in git or logs)"44ENVF=/etc/autoscholar/worker.env45if [ ! -f "$ENVF" ]; then46  { echo "CONTROL_PLANE_URL=$CONTROL_PLANE"; echo "WORKER_NAME=$NAME"; echo "WORKER_ROOT=/var/autoscholar"; echo "WORKER_MODE=real"; echo "WORKER_MAX_CONCURRENT=1"; echo "TECTONIC_BIN=/opt/homebrew/bin/tectonic"; echo "LATEX_TEMPLATE_DIR=/var/autoscholar/templates/latex"; echo "LOG_LEVEL=info"; [ -n "$SECRET" ] && echo "WORKER_ENROLLMENT_SECRET=$SECRET"; [ -n "$KEYFILE" ] && echo "ANTHROPIC_API_KEY=$(tr -d '\n' < "$KEYFILE")"; } > "$ENVF"47else48  echo "   $ENVF exists; not overwritten (edit by hand to rotate secrets)"49fi50chown root:asworker "$ENVF"; chmod 640 "$ENVF"51echo "== 6. pre-warm tectonic bundle cache as asworker"52cat > /var/autoscholar/cache/warm.tex <<'TEX'53\documentclass{article}54\usepackage{amsmath,amsthm,mathtools,booktabs,array,longtable,multirow,siunitx,graphicx,float,caption,subcaption,xcolor,fancyhdr,titlesec,enumitem,etoolbox,setspace,natbib,url,hyperref,cleveref,microtype,fontspec,unicode-math,iftex}55\setmainfont{LibertinusSerif}[Extension=.otf,UprightFont=*-Regular,BoldFont=*-Bold,ItalicFont=*-Italic,BoldItalicFont=*-BoldItalic]56\setsansfont{LibertinusSans}[Extension=.otf,UprightFont=*-Regular,BoldFont=*-Bold,ItalicFont=*-Italic]57\setmonofont{lmmonolt10-regular}[Extension=.otf,BoldFont=lmmonolt10-bold,ItalicFont=lmmonolt10-oblique]58\setmathfont{LibertinusMath-Regular.otf}59\begin{document}warm $x^2$ \cite{a}\bibliographystyle{plainnat}\bibliography{warm}\end{document}60TEX61echo '@misc{a, title={Warm}, author={A}, year={2026}}' > /var/autoscholar/cache/warm.bib62chown asworker:asworker /var/autoscholar/cache/warm.tex /var/autoscholar/cache/warm.bib63sudo -u asworker env HOME=/var/autoscholar/home TECTONIC_CACHE_DIR=/var/autoscholar/cache/tectonic PATH=/opt/homebrew/bin:/usr/bin:/bin bash -c 'cd /var/autoscholar/cache && /opt/homebrew/bin/tectonic -X compile warm.tex >/var/autoscholar/logs/tectonic-warm.log 2>&1 && echo "   bundle cached" || echo "   warm-up failed (see logs/tectonic-warm.log; will retry at first run)"'64echo "== 7. LaunchDaemon io.autoscholar.worker"65cat > /Library/LaunchDaemons/io.autoscholar.worker.plist <<PLIST66<?xml version="1.0" encoding="UTF-8"?>67<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">68<plist version="1.0"><dict>69  <key>Label</key><string>io.autoscholar.worker</string>70  <key>UserName</key><string>asworker</string>71  <key>GroupName</key><string>asworker</string>72  <key>ProgramArguments</key><array><string>/bin/bash</string><string>/var/autoscholar/bundle/install/run-worker.sh</string></array>73  <key>WorkingDirectory</key><string>/var/autoscholar</string>74  <key>RunAtLoad</key><true/>75  <key>KeepAlive</key><true/>76  <key>ThrottleInterval</key><integer>15</integer>77  <key>StandardOutPath</key><string>/var/autoscholar/logs/worker.out.log</string>78  <key>StandardErrorPath</key><string>/var/autoscholar/logs/worker.err.log</string>79  <key>EnvironmentVariables</key><dict><key>HOME</key><string>/var/autoscholar/home</string><key>PATH</key><string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string></dict>80  <key>SoftResourceLimits</key><dict><key>NumberOfFiles</key><integer>8192</integer></dict>81</dict></plist>82PLIST83chown root:wheel /Library/LaunchDaemons/io.autoscholar.worker.plist; chmod 644 /Library/LaunchDaemons/io.autoscholar.worker.plist84launchctl bootout system/io.autoscholar.worker 2>/dev/null || true85launchctl bootstrap system /Library/LaunchDaemons/io.autoscholar.worker.plist86launchctl kickstart -k system/io.autoscholar.worker87echo "== 8. pf egress rules for uid asworker (LAN/Tailscale/loopback blocked except control plane + DNS)"88CP_HOST=$(echo "$CONTROL_PLANE" | sed -E 's#https?://##; s#[:/].*##'); CP_PORT=$(echo "$CONTROL_PLANE" | sed -nE 's#https?://[^:/]+:([0-9]+).*#\1#p'); CP_PORT=${CP_PORT:-80}89cat > /etc/pf.anchors/io.autoscholar <<PF90# AutoScholar worker egress policy (uid asworker)91pass out quick proto tcp from any to $CP_HOST port $CP_PORT user asworker92pass out quick proto { tcp udp } from any to any port 53 user asworker93block out quick from any to { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10, 169.254.0.0/16 } user asworker94PF95grep -q 'anchor "io.autoscholar"' /etc/pf.conf || { echo 'anchor "io.autoscholar"'; echo 'load anchor "io.autoscholar" from "/etc/pf.anchors/io.autoscholar"'; } >> /etc/pf.conf96cat > /Library/LaunchDaemons/io.autoscholar.pf.plist <<PLIST97<?xml version="1.0" encoding="UTF-8"?>98<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">99<plist version="1.0"><dict>100  <key>Label</key><string>io.autoscholar.pf</string>101  <key>ProgramArguments</key><array><string>/bin/sh</string><string>-c</string><string>/sbin/pfctl -E; /sbin/pfctl -f /etc/pf.conf</string></array>102  <key>RunAtLoad</key><true/>103</dict></plist>104PLIST105launchctl bootout system/io.autoscholar.pf 2>/dev/null || true106launchctl bootstrap system /Library/LaunchDaemons/io.autoscholar.pf.plist107pfctl -E 2>/dev/null || true; pfctl -f /etc/pf.conf108pfctl -a io.autoscholar -s rules109echo "== done. Logs: /var/autoscholar/logs/worker.*.log ; status: sudo launchctl print system/io.autoscholar.worker | head"110
Files · bc25fcca6ef80397d231f256956ca5f0c907288f
Menu

Theme