Lane D deliverable: reproducible execution-plane deployment on a fresh Debian dedicated server (~12 cores, 40 GB RAM, single-node). Artifacts: - bootstrap.sh: Docker Engine, Bun, repo clone, build, systemd install, firewall (deny-by-default), optional Tailscale, disk monitor cron - .env.template: all 8 environment groups documented (Convex, Gitea, model gateway, AgentOS/RivetKit, Zopu agent, daemon, Docker sandbox, service auth) - systemd units: zopu-daemon, zopu-agent (both Restart=always), zopu-health timer (60s), zopu-docker-cleanup timer (daily) - scripts: health-check, update, rollback, docker-cleanup, disk-monitor - Caddyfile: optional reverse proxy (documentation-only by default) - README.md: full runbook with start/stop/log/update/rollback/cleanup procedures, topology documentation, and boundary notes Verified topology (from installed RivetKit source): - registry.start() boots an in-process RivetKit engine (envoy mode) with a native Rust sidecar at http://localhost:6420 (DEFAULT_ENDPOINT in chunk-YDUQHING.js line 4751). createClient() connects back to it. - No separate Rivet Engine process required for single-node operation. - Linux x64 sidecar packages (@rivet-dev/agentos-sidecar-linux-x64-gnu) are available as optional dependencies. Docker / Orb boundary: - Docker Engine installed and zopu user in docker group. - No Docker-backed sandbox code wired; Orb lane contract not yet landed. - Volume pruning omitted from docker-cleanup.sh to protect durable data. Validated: bash -n on all 6 shell scripts; mocked health-check smoke (nc-based TCP probes, mocked systemctl/docker); systemd unit structural checks. No JS/TS/agent files changed.
247 lines
8.8 KiB
Bash
Executable File
247 lines
8.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# bootstrap.sh — One-shot installer for the Zopu single-node execution plane.
|
|
#
|
|
# Run as root on a fresh Debian 12 host:
|
|
#
|
|
# sudo bash bootstrap.sh
|
|
#
|
|
# Environment overrides (set before running):
|
|
# ZOPU_REPO_URL — SSH clone URL (default: ssh://git@git.openputer.com:2222/puter/zopu-code.git)
|
|
# ZOPU_REPO_BRANCH — branch to deploy (default: dogfood/v0)
|
|
# ZOPU_INSTALL_DIR — install path (default: /opt/zopu)
|
|
# ZOPU_SERVICE_USER — system user (default: zopu)
|
|
# TAILSCALE_AUTHKEY — if set, configure Tailscale
|
|
# TAILSCALE_HOSTNAME — Tailscale hostname (default: zopu-runtime)
|
|
#
|
|
# Installs: Docker Engine, Bun, clones the repo, runs bun install, builds the
|
|
# daemon and agent, creates a non-root service user, installs systemd units,
|
|
# and configures firewall/Tailscale defaults.
|
|
|
|
set -euo pipefail
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Configuration
|
|
# ---------------------------------------------------------------------------
|
|
REPO_URL="${ZOPU_REPO_URL:-ssh://git@git.openputer.com:2222/puter/zopu-code.git}"
|
|
REPO_BRANCH="${ZOPU_REPO_BRANCH:-dogfood/v0}"
|
|
INSTALL_DIR="${ZOPU_INSTALL_DIR:-/opt/zopu}"
|
|
SERVICE_USER="${ZOPU_SERVICE_USER:-zopu}"
|
|
DEPLOY_DIR="${INSTALL_DIR}/deploy/zopu-runtime"
|
|
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[bootstrap]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[bootstrap]${NC} $*"; }
|
|
err() { echo -e "${RED}[bootstrap]${NC} $*" >&2; }
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Pre-flight
|
|
# ---------------------------------------------------------------------------
|
|
if [[ "$EUID" -ne 0 ]]; then
|
|
err "This script must be run as root."
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -f /etc/debian_version ]]; then
|
|
log "Detected Debian $(cat /etc/debian_version)"
|
|
else
|
|
warn "This script targets Debian 12. Other distributions may need manual adjustments."
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. System packages
|
|
# ---------------------------------------------------------------------------
|
|
log "Updating apt and installing base packages..."
|
|
apt-get update -y
|
|
apt-get install -y \
|
|
ca-certificates \
|
|
curl \
|
|
gnupg \
|
|
ufw \
|
|
git \
|
|
jq \
|
|
openssh-client \
|
|
unattended-upgrades \
|
|
netcat-openbsd \
|
|
rsyslog
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Docker Engine
|
|
# ---------------------------------------------------------------------------
|
|
if ! command -v docker &>/dev/null; then
|
|
log "Installing Docker Engine..."
|
|
install -m 0755 -d /etc/apt/keyrings
|
|
curl -fsSL https://download.docker.com/linux/debian/gpg \
|
|
-o /etc/apt/keyrings/docker.asc
|
|
chmod a+r /etc/apt/keyrings/docker.asc
|
|
|
|
echo \
|
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
|
|
https://download.docker.com/linux/debian \
|
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
|
> /etc/apt/sources.list.d/docker.list
|
|
|
|
apt-get update -y
|
|
apt-get install -y \
|
|
docker-ce \
|
|
docker-ce-cli \
|
|
containerd.io \
|
|
docker-buildx-plugin \
|
|
docker-compose-plugin
|
|
else
|
|
log "Docker Engine already installed: $(docker --version)"
|
|
fi
|
|
|
|
systemctl enable --now docker
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Bun
|
|
# ---------------------------------------------------------------------------
|
|
if ! command -v bun &>/dev/null; then
|
|
log "Installing Bun..."
|
|
curl -fsSL https://bun.sh/install | bash
|
|
ln -sf /root/.bun/bin/bun /usr/local/bin/bun
|
|
else
|
|
log "Bun already installed: $(bun --version)"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. Service user
|
|
# ---------------------------------------------------------------------------
|
|
if ! id "$SERVICE_USER" &>/dev/null; then
|
|
log "Creating service user: $SERVICE_USER"
|
|
useradd -r -m -d "/home/$SERVICE_USER" -s /bin/bash "$SERVICE_USER"
|
|
fi
|
|
|
|
if ! id -nG "$SERVICE_USER" | grep -qw docker; then
|
|
usermod -aG docker "$SERVICE_USER"
|
|
log "Added $SERVICE_USER to docker group"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 5. Clone or update repository
|
|
# ---------------------------------------------------------------------------
|
|
if [[ -d "$INSTALL_DIR/.git" ]]; then
|
|
log "Repository exists at $INSTALL_DIR, fetching latest..."
|
|
cd "$INSTALL_DIR"
|
|
git fetch origin
|
|
git checkout "$REPO_BRANCH"
|
|
git reset --hard "origin/$REPO_BRANCH"
|
|
else
|
|
log "Cloning $REPO_URL (branch $REPO_BRANCH) into $INSTALL_DIR..."
|
|
git clone --branch "$REPO_BRANCH" "$REPO_URL" "$INSTALL_DIR"
|
|
cd "$INSTALL_DIR"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 6. Install dependencies and build
|
|
# ---------------------------------------------------------------------------
|
|
log "Running bun install..."
|
|
sudo -u "$SERVICE_USER" bun install
|
|
|
|
log "Building daemon binary..."
|
|
sudo -u "$SERVICE_USER" bun run build:daemon
|
|
|
|
log "Building agent service..."
|
|
sudo -u "$SERVICE_USER" bun run build:agents
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 7. Environment file
|
|
# ---------------------------------------------------------------------------
|
|
ENV_FILE="$INSTALL_DIR/.env"
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
log "Copying .env.template to .env — EDIT BEFORE STARTING SERVICES"
|
|
cp "$DEPLOY_DIR/.env.template" "$ENV_FILE"
|
|
chown "$SERVICE_USER":"$SERVICE_USER" "$ENV_FILE"
|
|
chmod 600 "$ENV_FILE"
|
|
warn "Edit $ENV_FILE with real values before starting services."
|
|
else
|
|
log ".env already exists at $ENV_FILE"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 8. Persistent log directory
|
|
# ---------------------------------------------------------------------------
|
|
LOG_DIR="/var/log/zopu"
|
|
mkdir -p "$LOG_DIR"
|
|
chown "$SERVICE_USER":"$SERVICE_USER" "$LOG_DIR"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 9. Install systemd units (substitute placeholders)
|
|
# ---------------------------------------------------------------------------
|
|
log "Installing systemd units..."
|
|
for unit in zopu-daemon.service zopu-agent.service \
|
|
zopu-health.timer zopu-health.service \
|
|
zopu-docker-cleanup.timer zopu-docker-cleanup.service; do
|
|
SRC="$DEPLOY_DIR/systemd/$unit"
|
|
DST="/etc/systemd/system/$unit"
|
|
if [[ -f "$SRC" ]]; then
|
|
sed \
|
|
-e "s|__INSTALL_DIR__|$INSTALL_DIR|g" \
|
|
-e "s|__SERVICE_USER__|$SERVICE_USER|g" \
|
|
"$SRC" > "$DST"
|
|
log " installed $unit"
|
|
fi
|
|
done
|
|
systemctl daemon-reload
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 10. Firewall (deny-by-default posture)
|
|
# ---------------------------------------------------------------------------
|
|
log "Configuring firewall..."
|
|
if ! ufw status 2>/dev/null | grep -q "Status: active"; then
|
|
ufw allow 22/tcp
|
|
ufw default deny incoming
|
|
ufw default allow outgoing
|
|
ufw --force enable
|
|
log "Firewall enabled: SSH (22) allowed, all other incoming denied."
|
|
warn "Agent and RivetKit ports are NOT exposed publicly."
|
|
warn "Access is via Tailscale or direct private IP only."
|
|
else
|
|
log "Firewall already active."
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 11. Tailscale (optional)
|
|
# ---------------------------------------------------------------------------
|
|
if [[ -n "${TAILSCALE_AUTHKEY:-}" ]]; then
|
|
log "Installing and configuring Tailscale..."
|
|
if ! command -v tailscaled &>/dev/null; then
|
|
curl -fsSL https://tailscale.com/install.sh | sh
|
|
fi
|
|
tailscale up --authkey "$TAILSCALE_AUTHKEY" \
|
|
--hostname "${TAILSCALE_HOSTNAME:-zopu-runtime}" \
|
|
--accept-routes
|
|
log "Tailscale configured: $(tailscale ip -4 2>/dev/null || echo 'waiting for IP')"
|
|
else
|
|
warn "TAILSCALE_AUTHKEY not set — skipping Tailscale setup."
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 12. Disk-space monitoring cron
|
|
# ---------------------------------------------------------------------------
|
|
log "Installing disk-space monitor (daily at 06:00)..."
|
|
CRON_LINE="0 6 * * * $DEPLOY_DIR/scripts/disk-monitor.sh --warn-percent 80 >> /var/log/zopu/disk-monitor.log 2>&1"
|
|
echo "$CRON_LINE" > /etc/cron.d/zopu-disk-monitor
|
|
chmod 644 /etc/cron.d/zopu-disk-monitor
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Done
|
|
# ---------------------------------------------------------------------------
|
|
log "Bootstrap complete."
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Edit $ENV_FILE with real values"
|
|
echo " 2. Start services:"
|
|
echo " systemctl start zopu-daemon zopu-agent"
|
|
echo " 3. Enable health monitoring:"
|
|
echo " systemctl enable --now zopu-health.timer zopu-docker-cleanup.timer"
|
|
echo " 4. Verify health:"
|
|
echo " $DEPLOY_DIR/scripts/health-check.sh"
|
|
echo ""
|
|
warn "Services are NOT started automatically. Edit .env first."
|