239 lines
7.9 KiB
Bash
Executable File
239 lines
7.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# hermes-backup.sh — Full Hermes backup to Wasabi S3
|
|
# Backs up everything needed to restore Hermes on a new server:
|
|
# - Config, env, auth, state, sessions, profiles, skills, scripts
|
|
# - SSH keys, AWS creds, mail passwords
|
|
# - Systemd service files
|
|
# Also creates a restore script in the backup.
|
|
|
|
set -euo pipefail
|
|
|
|
# Activate AWS CLI from venv (DR FIX 2026-07-11 — backup was failing with "aws: command not found" in cron)
|
|
if [ -f /opt/awscli-venv/bin/activate ]; then
|
|
source /opt/awscli-venv/bin/activate
|
|
fi
|
|
|
|
# Use root disk, not tmpfs — /tmp is 958M tmpfs that fills up
|
|
BACKUP_DIR="/root/.hermes/.backups/hermes-backup-$(date +%F)"
|
|
DEST_DIR="hermes-full-backup"
|
|
DATE_TAG=$(date +%F)
|
|
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
# --- Wasabi config ---
|
|
ENDPOINT="https://s3.us-east-1.wasabisys.com"
|
|
BUCKET="hermes-vps-backups" # S3 bucket for Hermes config backups
|
|
# Credentials from ~/.aws/credentials
|
|
|
|
HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
|
|
|
|
# Activate AWS CLI venv for cron runs (cron has minimal PATH)
|
|
if [ -f /opt/awscli-venv/bin/activate ]; then
|
|
source /opt/awscli-venv/bin/activate
|
|
fi
|
|
|
|
echo "[+] Creating backup at $BACKUP_DIR"
|
|
rm -rf "$BACKUP_DIR"
|
|
mkdir -p "$BACKUP_DIR/config"
|
|
mkdir -p "$BACKUP_DIR/ssh"
|
|
mkdir -p "$BACKUP_DIR/himalaya"
|
|
mkdir -p "$BACKUP_DIR/aws"
|
|
mkdir -p "$BACKUP_DIR/systemd"
|
|
|
|
# --- Core Hermes config ---
|
|
cp "$HERMES_HOME/config.yaml" "$BACKUP_DIR/config/"
|
|
cp "$HERMES_HOME/.env" "$BACKUP_DIR/config/"
|
|
cp "$HERMES_HOME/auth.json" "$BACKUP_DIR/config/" 2>/dev/null || true
|
|
|
|
# --- State database + sessions ---
|
|
echo "[+] Backing up state.db (${s:-large})..."
|
|
cp "$HERMES_HOME/state.db" "$BACKUP_DIR/" 2>/dev/null || echo "[!] No state.db"
|
|
cp -r "$HERMES_HOME/sessions" "$BACKUP_DIR/sessions" 2>/dev/null || echo "[!] No sessions dir"
|
|
|
|
# --- Skills ---
|
|
echo "[+] Backing up skills..."
|
|
cp -r "$HERMES_HOME/skills" "$BACKUP_DIR/skills" 2>/dev/null || echo "[!] No skills"
|
|
|
|
# --- Scripts ---
|
|
echo "[+] Backing up scripts..."
|
|
cp -r "$HERMES_HOME/scripts" "$BACKUP_DIR/scripts" 2>/dev/null || echo "[!] No scripts"
|
|
|
|
# --- Profiles (Anita etc.) ---
|
|
if [ -d "$HERMES_HOME/profiles" ]; then
|
|
echo "[+] Backing up profiles..."
|
|
# Exclude bulky cache/sandbox dirs
|
|
rsync -a --exclude 'audio_cache' --exclude 'image_cache' --exclude 'cache' --exclude 'sandboxes' --exclude 'bin' "$HERMES_HOME/profiles" "$BACKUP_DIR/profiles" 2>/dev/null || true
|
|
fi
|
|
|
|
# --- Cron jobs ---
|
|
cp -r "$HERMES_HOME/cron" "$BACKUP_DIR/cron" 2>/dev/null || echo "[!] No cron dir"
|
|
|
|
# --- Other JSON state files ---
|
|
for f in channel_directory.json gateway_state.json processes.json; do
|
|
[ -f "$HERMES_HOME/$f" ] && cp "$HERMES_HOME/$f" "$BACKUP_DIR/config/"
|
|
done
|
|
|
|
# --- SSH keys ---
|
|
for f in "$HOME/.ssh/wisp_rsa" "$HOME/.ssh/wisp_rsa.pub"; do
|
|
[ -f "$f" ] && cp "$f" "$BACKUP_DIR/ssh/"
|
|
done
|
|
|
|
# --- Mail passwords ---
|
|
if [ -d "$HOME/.config/himalaya" ]; then
|
|
cp -r "$HOME/.config/himalaya" "$BACKUP_DIR/himalaya/"
|
|
fi
|
|
|
|
# --- AWS/Wasabi credentials ---
|
|
if [ -f "$HOME/.aws/credentials" ]; then
|
|
cp "$HOME/.aws/credentials" "$BACKUP_DIR/aws/"
|
|
fi
|
|
|
|
# --- Systemd service files ---
|
|
# DR FIX: corrected path from ~/.config/systemd/user to /etc/systemd/system (2026-07-08)
|
|
if [ -d "/etc/systemd/system" ]; then
|
|
mkdir -p "$BACKUP_DIR/systemd"
|
|
cp /etc/systemd/system/hermes*.service "$BACKUP_DIR/systemd/" 2>/dev/null || true
|
|
cp /etc/systemd/system/mysql-tunnel.service "$BACKUP_DIR/systemd/" 2>/dev/null || true
|
|
cp /etc/systemd/system/ollama.service "$BACKUP_DIR/systemd/" 2>/dev/null || true
|
|
cp /etc/systemd/system/shark-game.service "$BACKUP_DIR/systemd/" 2>/dev/null || true
|
|
fi
|
|
|
|
# --- Caddyfile ---
|
|
# DR FIX: added /etc/caddy/Caddyfile backup (2026-07-08)
|
|
if [ -f "/etc/caddy/Caddyfile" ]; then
|
|
mkdir -p "$BACKUP_DIR/caddy"
|
|
cp /etc/caddy/Caddyfile "$BACKUP_DIR/caddy/"
|
|
fi
|
|
|
|
# --- Migration credentials ---
|
|
# DR FIX: added migration-creds.txt backup (2026-07-08)
|
|
if [ -f "$HERMES_HOME/migration-creds.txt" ]; then
|
|
mkdir -p "$BACKUP_DIR/config"
|
|
cp "$HERMES_HOME/migration-creds.txt" "$BACKUP_DIR/config/migration-creds.txt"
|
|
fi
|
|
|
|
# --- Generate restore script ---
|
|
cat > "$BACKUP_DIR/restore.sh" << 'RESTOREEOF'
|
|
#!/bin/bash
|
|
# restore.sh — Restore Hermes from this backup
|
|
# Run from the backup directory on the target server
|
|
|
|
set -euo pipefail
|
|
|
|
HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
|
|
|
|
echo "[+] Creating Hermes home..."
|
|
mkdir -p "$HERMES_HOME"
|
|
|
|
echo "[+] Restoring config..."
|
|
cp config.yaml "$HERMES_HOME/"
|
|
cp .env "$HERMES_HOME/" 2>/dev/null || true
|
|
cp auth.json "$HERMES_HOME/" 2>/dev/null || true
|
|
|
|
echo "[+] Restoring state.db..."
|
|
cp state.db "$HERMES_HOME/" 2>/dev/null || echo "[!] No state.db in backup"
|
|
|
|
echo "[+] Restoring sessions..."
|
|
cp -r sessions "$HERMES_HOME/" 2>/dev/null || true
|
|
|
|
echo "[+] Restoring skills..."
|
|
cp -r skills "$HERMES_HOME/" 2>/dev/null || true
|
|
|
|
echo "[+] Restoring scripts..."
|
|
cp -r scripts "$HERMES_HOME/" 2>/dev/null || true
|
|
|
|
echo "[+] Restoring cron..."
|
|
cp -r cron "$HERMES_HOME/" 2>/dev/null || true
|
|
|
|
echo "[+] Restoring profiles..."
|
|
cp -r profiles "$HERMES_HOME/" 2>/dev/null || true
|
|
|
|
echo "[+] Restoring JSON state..."
|
|
for f in channel_directory.json gateway_state.json processes.json; do
|
|
[ -f "config/$f" ] && cp "config/$f" "$HERMES_HOME/$f"
|
|
done
|
|
|
|
echo "[+] Restoring SSH keys..."
|
|
mkdir -p "$HOME/.ssh"
|
|
cp ssh/* "$HOME/.ssh/" 2>/dev/null || true
|
|
|
|
echo "[+] Restoring mail config..."
|
|
cp -r himalaya "$HOME/.config/himalaya" 2>/dev/null || true
|
|
|
|
echo "[+] Restoring AWS/Wasabi creds..."
|
|
mkdir -p "$HOME/.aws"
|
|
cp aws/credentials "$HOME/.aws/" 2>/dev/null || true
|
|
|
|
echo "[+] Restoring systemd services..."
|
|
# DR FIX: corrected path from ~/.config/systemd/user to /etc/systemd/system (2026-07-08)
|
|
if [ -d systemd ]; then
|
|
sudo mkdir -p /etc/systemd/system
|
|
sudo cp systemd/* /etc/systemd/system/ 2>/dev/null || true
|
|
sudo systemctl daemon-reload 2>/dev/null || true
|
|
fi
|
|
|
|
echo "[+] Restoring Caddyfile..."
|
|
# DR FIX: added Caddyfile restore (2026-07-08)
|
|
if [ -f caddy/Caddyfile ]; then
|
|
sudo mkdir -p /etc/caddy
|
|
sudo cp caddy/Caddyfile /etc/caddy/
|
|
fi
|
|
|
|
echo "[+] Restoring migration credentials..."
|
|
# DR FIX: added migration-creds.txt restore (2026-07-08)
|
|
if [ -f config/migration-creds.txt ]; then
|
|
cp config/migration-creds.txt "$HERMES_HOME/migration-creds.txt"
|
|
chmod 600 "$HERMES_HOME/migration-creds.txt"
|
|
fi
|
|
|
|
echo ""
|
|
echo "=== Restore complete! ==="
|
|
echo "Next steps:"
|
|
echo " 1. Run: hermes gateway restart"
|
|
echo " 2. Verify: hermes gateway status"
|
|
echo " 3. Check sessions: hermes sessions list"
|
|
echo " 4. Verify Anita's gateway if applicable"
|
|
RESTOREEOF
|
|
chmod +x "$BACKUP_DIR/restore.sh"
|
|
|
|
# --- Write a manifest ---
|
|
cat > "$BACKUP_DIR/MANIFEST.txt" << EOF
|
|
Hermes Full Backup
|
|
Date: $TIMESTAMP
|
|
Hermes home: $HERMES_HOME
|
|
|
|
Contents:
|
|
- config.yaml, .env, auth.json (core config)
|
|
- migration-creds.txt (migration credentials)
|
|
- state.db + sessions/ (full session history)
|
|
- skills/ (all installed skills)
|
|
- scripts/ (custom scripts)
|
|
- profiles/ (Anita and any other profiles)
|
|
- cron/ (scheduled job definitions)
|
|
- ssh/ (WISP SSH keys)
|
|
- himalaya/ (mail passwords)
|
|
- aws/ (Wasabi/AWS credentials)
|
|
- systemd/ (service files from /etc/systemd/system/)
|
|
- caddy/ (Caddyfile from /etc/caddy/)
|
|
- restore.sh (self-contained restore script)
|
|
EOF
|
|
|
|
echo "[+] Creating archive..."
|
|
ARCHIVE="/root/.hermes/.backups/hermes-full-backup-$DATE_TAG.tar.gz"
|
|
cd "$(dirname "$BACKUP_DIR")" && tar czf "$ARCHIVE" "$(basename "$BACKUP_DIR")"
|
|
|
|
echo "[+] Uploading to Wasabi bucket..."
|
|
aws s3 cp "$ARCHIVE" "s3://$BUCKET/$DEST_DIR/hermes-full-backup-$DATE_TAG.tar.gz" \
|
|
--endpoint-url "$ENDPOINT"
|
|
|
|
echo "[+] Uploading manifest..."
|
|
aws s3 cp "$BACKUP_DIR/MANIFEST.txt" "s3://$BUCKET/$DEST_DIR/hermes-full-backup-$DATE_TAG-manifest.txt" \
|
|
--endpoint-url "$ENDPOINT" --content-type text/plain
|
|
|
|
echo "[+] Cleaning up..."
|
|
rm -rf "$BACKUP_DIR" "$ARCHIVE"
|
|
|
|
echo ""
|
|
echo "=== Backup complete! ==="
|
|
echo "S3: s3://$BUCKET/$DEST_DIR/hermes-full-backup-$DATE_TAG.tar.gz"
|
|
echo "Size: $(du -sh "$BACKUP_DIR" 2>/dev/null | cut -f1 || echo 'N/A')"
|