#!/bin/bash # ================================================================ # The Door — Deploy Script # ================================================================ # The crisis front door. Deploy to VPS. # # Usage: # bash deploy/deploy.sh # Full deploy (swap + nginx + site + firewall + hermes service) # bash deploy/deploy.sh --site # Site files only (fast update) # bash deploy/deploy.sh --ssl # SSL setup only # bash deploy/deploy.sh --service # Install/restart hermes-gateway systemd service # bash deploy/deploy.sh --check # Verify deployment health # # This script is IDEMPOTENT — safe to run repeatedly. # Run on VPS as root: bash deploy/deploy.sh # ================================================================ set -euo pipefail DOMAIN="alexanderwhitestone.com" SITE_ROOT="/var/www/the-door" DEPLOY_DIR="$(cd "$(dirname "$0")/.." && pwd)" VPS_IP=$(curl -sf --max-time 5 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}') # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${GREEN}[+]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } err() { echo -e "${RED}[-]${NC} $*" >&2; } # ================================================================ # FUNCTIONS # ================================================================ setup_swap() { log "Checking swap..." if swapon --show 2>/dev/null | grep -q swap; then log "Swap already configured: $(swapon --show | head -1 | awk '{print $3}')" return 0 fi if [ -f /swapfile ]; then warn "Swapfile exists but not active — activating..." swapon /swapfile 2>/dev/null && log "Swap activated" || err "Failed to activate swap" return 0 fi log "Creating 2GB swap file..." fallocate -l 2G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile grep -q '/swapfile' /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab log "Swap configured: $(free -h | awk '/Swap/{print $2}')" } install_packages() { log "Installing packages..." apt-get update -qq apt-get install -y -qq nginx certbot python3-certbot-nginx ufw curl log "Packages installed" } deploy_site() { log "Deploying site files to ${SITE_ROOT}..." mkdir -p "${SITE_ROOT}" # Copy static files for f in index.html manifest.json sw.js about.html testimony.html; do if [ -f "${DEPLOY_DIR}/${f}" ]; then cp "${DEPLOY_DIR}/${f}" "${SITE_ROOT}/${f}" fi done # Copy system prompt (reference, not served) cp "${DEPLOY_DIR}/system-prompt.txt" "${SITE_ROOT}/system-prompt.txt" chown -R www-data:www-data "${SITE_ROOT}" chmod -R 755 "${SITE_ROOT}" log "Site files deployed: $(ls -la ${SITE_ROOT} | wc -l) files" } configure_nginx() { log "Configuring nginx..." # Deploy site config cp "${DEPLOY_DIR}/deploy/nginx.conf" /etc/nginx/sites-available/the-door # Add rate limit zone if not present if ! grep -q "limit_req_zone.*the_door_api" /etc/nginx/nginx.conf 2>/dev/null; then sed -i '/http {/a \ limit_req_zone $binary_remote_addr zone=the_door_api:10m rate=10r/m;' /etc/nginx/nginx.conf fi # Enable site, disable default ln -sf /etc/nginx/sites-available/the-door /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default # Test and reload if nginx -t 2>&1; then systemctl enable nginx systemctl reload nginx || systemctl start nginx log "nginx configured and running" else err "nginx config test failed!" nginx -t return 1 fi } setup_firewall() { log "Configuring firewall..." ufw allow 22/tcp comment 'SSH' ufw allow 80/tcp comment 'HTTP' ufw allow 443/tcp comment 'HTTPS' ufw --force enable log "Firewall configured: $(ufw status | grep -c ALLOW) rules active" } setup_ssl() { log "Checking SSL certificate..." if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then log "SSL certificate already exists" # Check expiry EXPIRY=$(openssl x509 -enddate -noout -in "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" 2>/dev/null | cut -d= -f2) log "Certificate expires: ${EXPIRY}" return 0 fi warn "No SSL certificate found." warn "Ensure DNS is pointed: ${DOMAIN} A record → ${VPS_IP}" warn "" warn "Then run manually:" warn " certbot --nginx -d ${DOMAIN} -d www.${DOMAIN}" warn "" # Attempt automated cert if DNS resolves correctly RESOLVED_IP=$(dig +short "${DOMAIN}" @8.8.8.8 2>/dev/null | head -1) if [ "${RESOLVED_IP}" = "${VPS_IP}" ]; then log "DNS resolves correctly — obtaining SSL certificate..." certbot --nginx -d "${DOMAIN}" -d "www.${DOMAIN}" \ --non-interactive --agree-tos --register-unsafely-without-email \ && log "SSL certificate obtained!" \ || warn "certbot failed — run manually" else warn "DNS not pointed yet (resolved: ${RESOLVED_IP}, expected: ${VPS_IP})" fi } setup_hermes_service() { log "Setting up Hermes Gateway systemd service..." # Create hermes user if it doesn't exist if ! id -u hermes >/dev/null 2>&1; then log "Creating hermes user..." useradd --system --shell /usr/sbin/nologin --home-dir /opt/hermes --create-home hermes fi # Create working directory mkdir -p /opt/hermes chown hermes:hermes /opt/hermes # Deploy systemd unit file cp "${DEPLOY_DIR}/deploy/hermes-gateway.service" /etc/systemd/system/hermes-gateway.service systemctl daemon-reload systemctl enable hermes-gateway # Start or restart the service if systemctl is-active --quiet hermes-gateway; then log "Restarting hermes-gateway service..." systemctl restart hermes-gateway else log "Starting hermes-gateway service..." systemctl start hermes-gateway || warn "Service start failed — ensure hermes binary is installed at /usr/local/bin/hermes" fi # Verify sleep 2 if systemctl is-active --quiet hermes-gateway; then log "hermes-gateway service is running" else warn "hermes-gateway service not running — check: journalctl -u hermes-gateway" fi } check_deployment() { echo "" echo "================================" echo " The Door — Deployment Status" echo "================================" echo "" # Swap echo -n "Swap: " if swapon --show 2>/dev/null | grep -q swap; then echo -e "${GREEN}OK${NC} — $(swapon --show | head -1 | awk '{print $3}')" else echo -e "${RED}MISSING${NC}" fi # nginx echo -n "nginx: " if systemctl is-active --quiet nginx 2>/dev/null; then echo -e "${GREEN}RUNNING${NC}" else echo -e "${RED}STOPPED${NC}" fi # Site files echo -n "Site files: " if [ -f "${SITE_ROOT}/index.html" ]; then echo -e "${GREEN}OK${NC} — $(ls -la ${SITE_ROOT}/index.html | awk '{print $5}') bytes" else echo -e "${RED}MISSING${NC}" fi # SSL echo -n "SSL cert: " if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then EXPIRY=$(openssl x509 -enddate -noout -in "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" | cut -d= -f2) echo -e "${GREEN}OK${NC} — expires ${EXPIRY}" else echo -e "${YELLOW}NOT SET${NC}" fi # Firewall echo -n "Firewall: " if ufw status 2>/dev/null | grep -q "Status: active"; then echo -e "${GREEN}ACTIVE${NC} — $(ufw status | grep -c ALLOW) rules" else echo -e "${RED}INACTIVE${NC}" fi # HTTP test echo -n "HTTP test: " if curl -sf --max-time 5 "http://localhost/" -o /dev/null 2>/dev/null; then echo -e "${GREEN}OK${NC}" else echo -e "${YELLOW}N/A${NC}" fi # API proxy test echo -n "API proxy: " if curl -sf --max-time 5 "http://localhost/health" -o /dev/null 2>/dev/null; then echo -e "${GREEN}OK${NC}" else echo -e "${YELLOW}Hermes not responding${NC} (may be expected)" fi # DNS echo -n "DNS: " RESOLVED_IP=$(dig +short "${DOMAIN}" @8.8.8.8 2>/dev/null | head -1) if [ "${RESOLVED_IP}" = "${VPS_IP}" ]; then echo -e "${GREEN}OK${NC} — ${DOMAIN} → ${RESOLVED_IP}" else echo -e "${YELLOW}NOT POINTED${NC} (resolved: ${RESOLVED_IP:-nothing}, expected: ${VPS_IP})" fi # Hermes gateway service echo -n "Hermes service: " if systemctl is-active --quiet hermes-gateway 2>/dev/null; then echo -e "${GREEN}RUNNING${NC}" elif systemctl is-enabled --quiet hermes-gateway 2>/dev/null; then echo -e "${YELLOW}ENABLED but not running${NC}" else echo -e "${RED}NOT INSTALLED${NC}" fi echo "" echo "IP: ${VPS_IP}" echo "Domain: ${DOMAIN}" echo "Site root: ${SITE_ROOT}" } # ================================================================ # MAIN # ================================================================ echo "" echo "=== The Door — Deployment ===" echo "Deploy dir: ${DEPLOY_DIR}" echo "VPS IP: ${VPS_IP}" echo "" case "${1:-full}" in --site) deploy_site configure_nginx ;; --ssl) setup_ssl ;; --service) setup_hermes_service ;; --check) check_deployment ;; --full|"") setup_swap install_packages deploy_site configure_nginx setup_firewall setup_ssl setup_hermes_service check_deployment ;; *) echo "Usage: $0 [--site|--ssl|--service|--check|--full]" exit 1 ;; esac echo "" echo "=== Deployment complete ===" echo "" echo "Next steps:" echo " 1. Point DNS: ${DOMAIN} A record → ${VPS_IP}" echo " 2. If SSL not set: certbot --nginx -d ${DOMAIN} -d www.${DOMAIN}" echo " 3. Test: curl -I https://${DOMAIN}" echo " 4. Test API: curl https://${DOMAIN}/api/health" echo "" echo "The Door is open."