#!/bin/bash # Bootstrap script for Materia supervisor instance. # Run once on a fresh server after setup_server.sh. # # Usage: # ssh root@ 'bash -s' < infra/bootstrap_supervisor.sh # # Prerequisites: # - age keypair exists at /opt/materia/age-key.txt # (or SOPS_AGE_KEY_FILE env var pointing elsewhere) # - The server age public key is already in .sops.yaml and .env.prod.sops # (run setup_server.sh first, then add the key and re-commit) # - GITLAB_READ_TOKEN is set (GitLab project access token, read-only) set -euo pipefail echo "=== Materia Supervisor Bootstrap ===" echo "This script will:" echo " 1. Install dependencies (git, uv, sops, age)" echo " 2. Clone the materia repository" echo " 3. Decrypt secrets from .env.prod.sops" echo " 4. Set up systemd service" echo " 5. Start the supervisor" echo "" if [ "$EUID" -ne 0 ]; then echo "ERROR: This script must be run as root" exit 1 fi # ── Configuration ────────────────────────────────────────── REPO_DIR="/opt/materia" GITLAB_PROJECT="deemanone/materia" AGE_KEY_FILE="${SOPS_AGE_KEY_FILE:-$REPO_DIR/age-key.txt}" if [ -z "${GITLAB_READ_TOKEN:-}" ]; then echo "ERROR: GITLAB_READ_TOKEN not set" echo " export GITLAB_READ_TOKEN=" exit 1 fi REPO_URL="https://oauth2:${GITLAB_READ_TOKEN}@gitlab.com/${GITLAB_PROJECT}.git" # ── System dependencies ──────────────────────────────────── echo "--- Installing system dependencies ---" apt-get update -q apt-get install -y -q git curl ca-certificates # ── uv ───────────────────────────────────────────────────── echo "--- Installing uv ---" if ! command -v uv &>/dev/null; then curl -LsSf https://astral.sh/uv/install.sh | sh export PATH="$HOME/.local/bin:$PATH" echo 'export PATH="$HOME/.local/bin:$PATH"' >> /root/.bashrc fi # ── sops + age ───────────────────────────────────────────── echo "--- Installing sops + age ---" ARCH=$(uname -m) case "$ARCH" in x86_64) ARCH_SOPS="amd64"; ARCH_AGE="amd64" ;; aarch64) ARCH_SOPS="arm64"; ARCH_AGE="arm64" ;; *) echo "Unsupported architecture: $ARCH"; exit 1 ;; esac if ! command -v age &>/dev/null; then AGE_VERSION="v1.3.1" curl -fsSL "https://dl.filippo.io/age/${AGE_VERSION}?for=linux/${AGE_ARCH}" -o /tmp/age.tar.gz tar -xzf /tmp/age.tar.gz -C /usr/local/bin --strip-components=1 age/age age/age-keygen chmod +x /usr/local/bin/age /usr/local/bin/age-keygen rm /tmp/age.tar.gz fi if ! command -v sops &>/dev/null; then SOPS_VERSION="v3.12.1" curl -fsSL "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.${ARCH_SOPS}" -o /usr/local/bin/sops chmod +x /usr/local/bin/sops fi # ── Clone repository ─────────────────────────────────────── echo "--- Cloning repository ---" if [ -d "$REPO_DIR/.git" ]; then echo "Repository already exists — fetching latest tags..." cd "$REPO_DIR" git fetch --tags --prune-tags origin else git clone "$REPO_URL" "$REPO_DIR" cd "$REPO_DIR" fi # Checkout latest release tag (same logic as supervisor) LATEST_TAG=$(git tag --list --sort=-version:refname "v*" | head -1) if [ -n "$LATEST_TAG" ]; then echo "Checking out $LATEST_TAG..." git checkout --detach "$LATEST_TAG" else echo "No release tags found — staying on current HEAD" fi # ── Check age keypair ────────────────────────────────────── echo "--- Checking age keypair ---" if [ ! -f "$AGE_KEY_FILE" ]; then echo "ERROR: Age keypair not found at $AGE_KEY_FILE" echo "" echo "Run infra/setup_server.sh first to generate the keypair, then:" echo " 1. Copy the public key from setup_server.sh output" echo " 2. Add it to .sops.yaml on your workstation" echo " 3. Run: sops updatekeys .env.prod.sops" echo " 4. Commit + push and re-run this bootstrap" exit 1 fi export SOPS_AGE_KEY_FILE="$AGE_KEY_FILE" # ── Decrypt secrets ──────────────────────────────────────── echo "--- Decrypting secrets from .env.prod.sops ---" sops --input-type dotenv --output-type dotenv -d "$REPO_DIR/.env.prod.sops" > "$REPO_DIR/.env" chmod 600 "$REPO_DIR/.env" echo "Secrets written to $REPO_DIR/.env" # ── Data directories ─────────────────────────────────────── echo "--- Creating data directories ---" mkdir -p /data/materia/landing # ── Python dependencies ──────────────────────────────────── echo "--- Installing Python dependencies ---" cd "$REPO_DIR" uv sync --all-packages # ── Systemd service ──────────────────────────────────────── echo "--- Setting up systemd service ---" cp "$REPO_DIR/infra/supervisor/materia-supervisor.service" /etc/systemd/system/materia-supervisor.service echo "--- Enabling and starting service ---" systemctl daemon-reload systemctl enable materia-supervisor systemctl restart materia-supervisor echo "" echo "=== Bootstrap complete! ===" echo "" echo "Check status: systemctl status materia-supervisor" echo "View logs: journalctl -u materia-supervisor -f" echo "Workflow status: cd $REPO_DIR && uv run python src/materia/supervisor.py status" echo "" echo "Repo: $REPO_DIR" echo "Tag: $(cd $REPO_DIR && git describe --tags --always)"