diff --git a/aurora/.stignore b/aurora/.stignore deleted file mode 100644 index 7982c46..0000000 --- a/aurora/.stignore +++ /dev/null @@ -1,65 +0,0 @@ -// ============================================= -// Syncthing excludes for home directory sync -// Everything NOT listed here will be synced -// ============================================= - -// --- Caches (all regeneratable) --- -.cache -.bun -.npm -.nvm -go -.local/share/Trash - -// --- Package managers / build artifacts --- -**/node_modules -**/__pycache__ -**/.venv -**/target -**/.tox -**/.mypy_cache -**/.pytest_cache -**/dist -**/build -**/.next -**/.turbo - -// --- AI tool session logs (large, not portable) --- -.local/share/opencode -.local/share/claude -.opencode -.claude - -// --- Containers (rebuild per-machine) --- -.local/share/containers - -// --- Flatpak app data (reinstall regenerates) --- -.var/app/com.valvesoftware.Steam -.var/app/com.microsoft.Edge -.var/app/com.google.Chrome -.var/app/com.stremio.Stremio -.var/app/org.mozilla.firefox - -// --- Machine-specific / KDE desktop state --- -.local/share/baloo -.local/share/kactivitymanagerd -.local/share/klipper -.local/share/recently-used.xbel -auto-cpufreq - -// --- SQLite DBs (live sync corrupts) --- -.open-webui - -// --- Transient --- -Downloads - -// --- Syncthing own data --- -.config/syncthing -.local/state/syncthing - -// --- Common junk patterns --- -*.swp -*.swo -*~ -.DS_Store -Thumbs.db diff --git a/aurora/99-backup-portable.rules b/aurora/99-backup-portable.rules deleted file mode 100644 index b3722b7..0000000 --- a/aurora/99-backup-portable.rules +++ /dev/null @@ -1,2 +0,0 @@ -# Trigger backup when Seagate Portable drive is connected -ACTION=="add", SUBSYSTEM=="block", ENV{ID_SERIAL_SHORT}=="NT3D9HDX", ENV{DEVTYPE}=="partition", TAG+="systemd", ENV{SYSTEMD_WANTS}="backup-portable.service" diff --git a/aurora/backup-portable.service b/aurora/backup-portable.service deleted file mode 100644 index e42c375..0000000 --- a/aurora/backup-portable.service +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -Description=Backup home to Portable drive (BTRFS send/receive) -# Give the drive a moment to fully settle after plug-in -After=local-fs.target - -[Service] -Type=oneshot -ExecStartPre=/bin/sleep 5 -ExecStart=/usr/local/bin/backup-to-portable -TimeoutStartSec=7200 -Nice=10 -IOSchedulingClass=idle -IOSchedulingPriority=7 -# Don't kill backup if user logs out -KillMode=process - -[Install] -WantedBy=multi-user.target diff --git a/aurora/backup-to-portable b/aurora/backup-to-portable deleted file mode 100755 index e7d8f7a..0000000 --- a/aurora/backup-to-portable +++ /dev/null @@ -1,199 +0,0 @@ -#!/bin/bash -# ============================================================================= -# Backup to Portable Drive -# BTRFS snapshot + send/receive with LUKS encryption -# For Aurora (Universal Blue) on BTRFS -# ============================================================================= -set -euo pipefail - -# --- Configuration --- -LUKS_DEVICE="/dev/disk/by-id/usb-Seagate_Portable_NT3D9HDX-0:0-part1" -LUKS_KEYFILE="/etc/backup-drive.key" -LUKS_NAME="backup-drive" -BACKUP_MOUNT="/mnt/backup-drive" -BTRFS_DEVICE="/dev/nvme0n1p3" -BTRFS_TOP="/mnt/btrfs-root" -SNAP_DIR="snapshots" -KEEP_LOCAL=2 # local snapshots (need at least 1 for incremental parent) -KEEP_REMOTE=10 # remote snapshots on backup drive -TIMESTAMP=$(date +%Y%m%d-%H%M%S) -LOGFILE="/var/log/backup-portable.log" -LOCK="/run/backup-portable.lock" - -# --- Helpers --- -log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"; } - -notify() { - local urgency="${3:-normal}" - local uid bus - uid=$(id -u joey 2>/dev/null) || return 0 - bus="unix:path=/run/user/${uid}/bus" - sudo -u joey DBUS_SESSION_BUS_ADDRESS="$bus" \ - notify-send -u "$urgency" -i drive-removable-media "$1" "$2" 2>/dev/null || true -} - -die() { log "ERROR: $*"; notify "Backup Failed" "$*" critical; exit 1; } - -cleanup() { - log "Cleaning up mounts..." - mountpoint -q "$BACKUP_MOUNT" 2>/dev/null && umount "$BACKUP_MOUNT" || true - mountpoint -q "$BTRFS_TOP" 2>/dev/null && umount "$BTRFS_TOP" || true - [ -e "/dev/mapper/$LUKS_NAME" ] && cryptsetup luksClose "$LUKS_NAME" 2>/dev/null || true - rm -f "$LOCK" -} -trap cleanup EXIT - -# --- Preflight --- -[ "$(id -u)" -eq 0 ] || die "Must run as root" - -# Single-instance lock -if [ -f "$LOCK" ]; then - pid=$(cat "$LOCK" 2>/dev/null || true) - if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then - die "Backup already running (PID $pid)" - fi - log "Removing stale lock" - rm -f "$LOCK" -fi -echo $$ > "$LOCK" - -# Check drive is present -[ -e "$LUKS_DEVICE" ] || die "Backup drive not connected" - -log "=========================================" -log "Starting backup: $TIMESTAMP" -log "=========================================" -notify "Backup Started — Do NOT unplug drive" "Backing up home to Portable drive..." critical - -# --- Open LUKS --- -if [ ! -e "/dev/mapper/$LUKS_NAME" ]; then - log "Opening LUKS volume..." - cryptsetup luksOpen --key-file "$LUKS_KEYFILE" "$LUKS_DEVICE" "$LUKS_NAME" \ - || die "Failed to open LUKS volume" -fi - -# --- Mount filesystems --- -mkdir -p "$BACKUP_MOUNT" "$BTRFS_TOP" - -if ! mountpoint -q "$BACKUP_MOUNT"; then - mount /dev/mapper/"$LUKS_NAME" "$BACKUP_MOUNT" \ - || die "Failed to mount backup drive" -fi - -if ! mountpoint -q "$BTRFS_TOP"; then - mount -o subvolid=5 "$BTRFS_DEVICE" "$BTRFS_TOP" \ - || die "Failed to mount source BTRFS top-level" -fi - -mkdir -p "$BTRFS_TOP/$SNAP_DIR" - -# --- Create read-only snapshot of home --- -SNAP_NAME="home-$TIMESTAMP" -log "Creating snapshot: $SNAP_NAME" -btrfs subvolume snapshot -r "$BTRFS_TOP/home" "$BTRFS_TOP/$SNAP_DIR/$SNAP_NAME" \ - || die "Failed to create snapshot" - -# --- Send snapshot to backup drive --- -# Find parent for incremental send: latest snapshot that exists on BOTH local and remote -PARENT_FLAG="" -LATEST_REMOTE=$(ls -1d "$BACKUP_MOUNT/backups"/home-* 2>/dev/null | sort | tail -1 || true) -if [ -n "$LATEST_REMOTE" ]; then - PARENT_NAME=$(basename "$LATEST_REMOTE") - if [ -d "$BTRFS_TOP/$SNAP_DIR/$PARENT_NAME" ]; then - PARENT_FLAG="-p $BTRFS_TOP/$SNAP_DIR/$PARENT_NAME" - log "Incremental send (parent: $PARENT_NAME)" - else - log "Parent snapshot not found locally, falling back to full send" - fi -else - log "No remote snapshots found, performing full send" -fi - -log "Sending snapshot to backup drive..." -# shellcheck disable=SC2086 -btrfs send $PARENT_FLAG "$BTRFS_TOP/$SNAP_DIR/$SNAP_NAME" \ - | btrfs receive "$BACKUP_MOUNT/backups/" \ - || die "btrfs send/receive failed" - -log "Snapshot sent successfully" - -# --- Save metadata --- -log "Saving metadata..." -META_DIR="$BACKUP_MOUNT/metadata/$TIMESTAMP" -mkdir -p "$META_DIR" - -# Flatpak apps -flatpak list --app --columns=application > "$META_DIR/flatpak-apps.txt" 2>/dev/null || true - -# rpm-ostree status -rpm-ostree status > "$META_DIR/rpm-ostree-status.txt" 2>/dev/null || true - -# Layered packages -rpm-ostree status --json 2>/dev/null | python3 -c " -import sys, json -try: - d = json.load(sys.stdin) - pkgs = d.get('deployments', [{}])[0].get('requested-packages', []) - print('\n'.join(pkgs)) -except: pass -" > "$META_DIR/layered-packages.txt" 2>/dev/null || true - -# /etc (small but important on ostree systems) -tar czf "$META_DIR/etc-backup.tar.gz" -C / etc/ 2>/dev/null || true - -# Hostname + date for identification -echo "$(hostname) - $(date)" > "$META_DIR/info.txt" - -# --- Cleanup old local snapshots --- -# Keep KEEP_LOCAL most recent, but ALWAYS keep the one matching latest remote -log "Cleaning up old local snapshots..." -mapfile -t LOCAL_SNAPS < <(ls -1d "$BTRFS_TOP/$SNAP_DIR"/home-* 2>/dev/null | sort) -LOCAL_COUNT=${#LOCAL_SNAPS[@]} - -if [ "$LOCAL_COUNT" -gt "$KEEP_LOCAL" ]; then - # The latest local snap is the one we just created, keep it - # Also keep the most recent remote's parent if different - for ((i=0; i < LOCAL_COUNT - KEEP_LOCAL; i++)); do - OLD_SNAP="${LOCAL_SNAPS[$i]}" - OLD_NAME=$(basename "$OLD_SNAP") - # Don't delete if it's still needed as a remote parent reference - if [ -d "$BACKUP_MOUNT/backups/$OLD_NAME" ]; then - log "Keeping local snapshot $OLD_NAME (exists on remote)" - continue - fi - log "Deleting old local snapshot: $OLD_NAME" - btrfs subvolume delete "$OLD_SNAP" 2>/dev/null || true - done -fi - -# --- Cleanup old remote snapshots --- -log "Cleaning up old remote snapshots..." -mapfile -t REMOTE_SNAPS < <(ls -1d "$BACKUP_MOUNT/backups"/home-* 2>/dev/null | sort) -REMOTE_COUNT=${#REMOTE_SNAPS[@]} - -if [ "$REMOTE_COUNT" -gt "$KEEP_REMOTE" ]; then - for ((i=0; i < REMOTE_COUNT - KEEP_REMOTE; i++)); do - OLD_REMOTE="${REMOTE_SNAPS[$i]}" - log "Deleting old remote snapshot: $(basename "$OLD_REMOTE")" - btrfs subvolume delete "$OLD_REMOTE" 2>/dev/null || true - done -fi - -# Cleanup old metadata dirs -mapfile -t META_DIRS < <(ls -1d "$BACKUP_MOUNT/metadata"/20* 2>/dev/null | sort) -META_COUNT=${#META_DIRS[@]} -if [ "$META_COUNT" -gt "$KEEP_REMOTE" ]; then - for ((i=0; i < META_COUNT - KEEP_REMOTE; i++)); do - rm -rf "${META_DIRS[$i]}" - done -fi - -# --- Final sync --- -log "Syncing drive..." -sync -USAGE=$(df -h /dev/mapper/"$LUKS_NAME" | tail -1 | awk '{print $3 " used / " $2 " total (" $5 ")"}') -DURATION=$(( $(date +%s) - $(date -d "${TIMESTAMP:0:8} ${TIMESTAMP:9:2}:${TIMESTAMP:11:2}:${TIMESTAMP:13:2}" +%s) )) -DURATION_MIN=$(( DURATION / 60 )) -log "Backup complete: $SNAP_NAME (${DURATION_MIN}m)" -log "Drive usage: $USAGE" -notify "Backup Complete — Safe to unplug" "Snapshot: $SNAP_NAME\nDrive: $USAGE\nDuration: ${DURATION_MIN} min" critical diff --git a/aurora/restore-from-portable b/aurora/restore-from-portable deleted file mode 100755 index 574bbde..0000000 --- a/aurora/restore-from-portable +++ /dev/null @@ -1,352 +0,0 @@ -#!/bin/bash -# ============================================================================= -# Restore from Portable Backup Drive -# Browse snapshots, mount for file recovery, or full home restore -# ============================================================================= -set -euo pipefail - -# --- Configuration --- -LUKS_DEVICE="/dev/disk/by-id/usb-Seagate_Portable_NT3D9HDX-0:0-part1" -LUKS_KEYFILE="/etc/backup-drive.key" -LUKS_NAME="backup-drive" -BACKUP_MOUNT="/mnt/backup-drive" -BTRFS_DEVICE="/dev/nvme0n1p3" -BTRFS_TOP="/mnt/btrfs-root" -BROWSE_MOUNT="/mnt/backup-browse" - -# --- Helpers --- -bold() { echo -e "\033[1m$*\033[0m"; } -green() { echo -e "\033[32m$*\033[0m"; } -red() { echo -e "\033[31m$*\033[0m"; } -yellow(){ echo -e "\033[33m$*\033[0m"; } - -die() { red "ERROR: $*"; exit 1; } - -cleanup() { - mountpoint -q "$BROWSE_MOUNT" 2>/dev/null && umount "$BROWSE_MOUNT" 2>/dev/null || true - # Don't auto-close LUKS/unmount backup in restore mode — user may still need it -} -trap cleanup EXIT - -# --- Preflight --- -[ "$(id -u)" -eq 0 ] || die "Must run as root (sudo)" -[ -e "$LUKS_DEVICE" ] || die "Backup drive not connected" - -# --- Open LUKS + Mount --- -if [ ! -e "/dev/mapper/$LUKS_NAME" ]; then - echo "Opening LUKS volume..." - if [ -f "$LUKS_KEYFILE" ]; then - cryptsetup luksOpen --key-file "$LUKS_KEYFILE" "$LUKS_DEVICE" "$LUKS_NAME" - else - echo "Keyfile not found. Enter passphrase:" - cryptsetup luksOpen "$LUKS_DEVICE" "$LUKS_NAME" - fi -fi - -mkdir -p "$BACKUP_MOUNT" -if ! mountpoint -q "$BACKUP_MOUNT"; then - mount /dev/mapper/"$LUKS_NAME" "$BACKUP_MOUNT" -fi - -# --- List snapshots --- -list_snapshots() { - bold "\nAvailable backup snapshots:" - echo "─────────────────────────────────────────" - local i=1 - mapfile -t SNAPSHOTS < <(ls -1d "$BACKUP_MOUNT/backups"/home-* 2>/dev/null | sort -r) - - if [ ${#SNAPSHOTS[@]} -eq 0 ]; then - die "No snapshots found on backup drive" - fi - - for snap in "${SNAPSHOTS[@]}"; do - local name date_str - name=$(basename "$snap") - # Parse timestamp from name: home-YYYYMMDD-HHMMSS - date_str=$(echo "$name" | sed 's/home-//' | sed 's/\([0-9]\{8\}\)-\([0-9]\{2\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\1 \2:\3:\4/') - printf " %2d) %s (%s)\n" "$i" "$name" "$date_str" - ((i++)) - done - echo "" -} - -# --- Show metadata for a snapshot --- -show_metadata() { - local snap_name="$1" - local ts="${snap_name#home-}" - local meta_dir="$BACKUP_MOUNT/metadata/$ts" - - if [ -d "$meta_dir" ]; then - bold "\nMetadata for $snap_name:" - echo "─────────────────────────────────────────" - [ -f "$meta_dir/info.txt" ] && echo " Host: $(cat "$meta_dir/info.txt")" - [ -f "$meta_dir/flatpak-apps.txt" ] && echo " Flatpaks: $(wc -l < "$meta_dir/flatpak-apps.txt") apps" - [ -f "$meta_dir/layered-packages.txt" ] && echo " Layered pkgs: $(cat "$meta_dir/layered-packages.txt" | grep -c . || echo 0)" - [ -f "$meta_dir/etc-backup.tar.gz" ] && echo " /etc backup: $(du -h "$meta_dir/etc-backup.tar.gz" | cut -f1)" - echo "" - else - yellow " (no metadata for this snapshot)" - fi -} - -# --- Browse a snapshot (mount read-only) --- -browse_snapshot() { - local snap_path="$1" - local snap_name - snap_name=$(basename "$snap_path") - - mkdir -p "$BROWSE_MOUNT" - if mountpoint -q "$BROWSE_MOUNT"; then - umount "$BROWSE_MOUNT" - fi - - # Bind-mount the snapshot for easy browsing - mount --bind "$snap_path" "$BROWSE_MOUNT" - - green "\nSnapshot mounted read-only at: $BROWSE_MOUNT" - echo "" - echo "You can now browse and copy files:" - echo " ls $BROWSE_MOUNT/" - echo " cp $BROWSE_MOUNT/joey/Documents/file.txt ~/Documents/" - echo "" - yellow "When done, run: sudo umount $BROWSE_MOUNT" - echo "Or press Enter here to unmount and return to menu." - read -r - umount "$BROWSE_MOUNT" 2>/dev/null || true -} - -# --- Restore specific files/directories --- -restore_files() { - local snap_path="$1" - local snap_name - snap_name=$(basename "$snap_path") - - echo "" - bold "Restore files from $snap_name" - echo "Enter path relative to home (e.g., joey/Documents, joey/.config/Code):" - read -r rel_path - - [ -z "$rel_path" ] && { yellow "No path entered."; return; } - - local src="$snap_path/$rel_path" - if [ ! -e "$src" ]; then - red "Path not found in snapshot: $rel_path" - return - fi - - local dest_base="/var/home" - local dest="$dest_base/$rel_path" - - echo "" - echo "Source: $src" - echo "Destination: $dest" - echo "" - - if [ -e "$dest" ]; then - yellow "WARNING: Destination exists and will be overwritten." - echo -n "Create backup of current version first? [Y/n] " - read -r yn - if [ "$yn" != "n" ] && [ "$yn" != "N" ]; then - local backup="${dest}.pre-restore-$(date +%Y%m%d-%H%M%S)" - cp -a "$dest" "$backup" - green "Current version backed up to: $backup" - fi - fi - - echo -n "Proceed with restore? [y/N] " - read -r confirm - if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then - if [ -d "$src" ]; then - mkdir -p "$(dirname "$dest")" - rsync -a --delete "$src/" "$dest/" - else - mkdir -p "$(dirname "$dest")" - cp -a "$src" "$dest" - fi - # Restore ownership to joey - chown -R joey:joey "$dest" - green "Restored: $rel_path" - else - yellow "Cancelled." - fi -} - -# --- Full home restore via btrfs send/receive --- -full_restore() { - local snap_path="$1" - local snap_name - snap_name=$(basename "$snap_path") - - echo "" - red "╔══════════════════════════════════════════════════════╗" - red "║ FULL HOME RESTORE — THIS IS DESTRUCTIVE ║" - red "║ Your current /var/home will be replaced entirely. ║" - red "║ A snapshot of the current state will be saved. ║" - red "╚══════════════════════════════════════════════════════╝" - echo "" - echo "Restoring from: $snap_name" - echo "" - echo -n "Type 'RESTORE' to confirm: " - read -r confirm - [ "$confirm" = "RESTORE" ] || { yellow "Cancelled."; return; } - - # Mount top-level BTRFS - mkdir -p "$BTRFS_TOP" - if ! mountpoint -q "$BTRFS_TOP"; then - mount -o subvolid=5 "$BTRFS_DEVICE" "$BTRFS_TOP" - fi - - # Snapshot current home as safety net - local safety_name="home-pre-restore-$(date +%Y%m%d-%H%M%S)" - echo "Creating safety snapshot of current home: $safety_name" - btrfs subvolume snapshot -r "$BTRFS_TOP/home" "$BTRFS_TOP/snapshots/$safety_name" - green "Safety snapshot created: $safety_name" - - # Strategy: receive the backup snapshot, then swap subvolumes - echo "Receiving snapshot from backup drive..." - local restore_name="home-restoring-$(date +%Y%m%d-%H%M%S)" - - btrfs send "$snap_path" | btrfs receive "$BTRFS_TOP/snapshots/" \ - || die "btrfs receive failed" - - # The received snapshot is read-only. Create a writable snapshot from it. - echo "Creating writable home from snapshot..." - - # Rename current home subvolume out of the way - mv "$BTRFS_TOP/home" "$BTRFS_TOP/home-old-$(date +%Y%m%d-%H%M%S)" - - # Create writable snapshot as new home - btrfs subvolume snapshot "$BTRFS_TOP/snapshots/$snap_name" "$BTRFS_TOP/home" - - green "" - green "═══════════════════════════════════════════" - green " Full restore complete!" - green " Old home saved as: home-old-*" - green " REBOOT to use restored home." - green "═══════════════════════════════════════════" - echo "" - yellow "After reboot, once verified, clean up old home with:" - echo " sudo mount -o subvolid=5 /dev/nvme0n1p3 /mnt/btrfs-root" - echo " sudo btrfs subvolume delete /mnt/btrfs-root/home-old-*" - echo "" - - umount "$BTRFS_TOP" 2>/dev/null || true -} - -# --- Restore system metadata --- -restore_metadata() { - local snap_name="$1" - local ts="${snap_name#home-}" - local meta_dir="$BACKUP_MOUNT/metadata/$ts" - - [ -d "$meta_dir" ] || { red "No metadata found for $snap_name"; return; } - - bold "\nRestore system metadata from $snap_name" - echo "─────────────────────────────────────────" - echo " 1) Reinstall flatpak apps" - echo " 2) Reinstall layered rpm-ostree packages" - echo " 3) Restore /etc from backup" - echo " 4) Show all metadata (view only)" - echo " b) Back" - echo "" - echo -n "Choice: " - read -r choice - - case "$choice" in - 1) - if [ -f "$meta_dir/flatpak-apps.txt" ]; then - echo "Installing flatpak apps..." - while IFS= read -r app; do - [ -z "$app" ] && continue - echo " Installing: $app" - flatpak install -y flathub "$app" 2>/dev/null || yellow " Failed: $app" - done < "$meta_dir/flatpak-apps.txt" - green "Done." - fi - ;; - 2) - if [ -f "$meta_dir/layered-packages.txt" ] && [ -s "$meta_dir/layered-packages.txt" ]; then - echo "Layered packages to install:" - cat "$meta_dir/layered-packages.txt" - echo "" - echo -n "Install all? [y/N] " - read -r yn - if [ "$yn" = "y" ] || [ "$yn" = "Y" ]; then - # shellcheck disable=SC2046 - rpm-ostree install $(cat "$meta_dir/layered-packages.txt" | tr '\n' ' ') - yellow "Reboot required to apply layered packages." - fi - else - yellow "No layered packages recorded." - fi - ;; - 3) - if [ -f "$meta_dir/etc-backup.tar.gz" ]; then - red "WARNING: This will overwrite files in /etc" - echo -n "Proceed? [y/N] " - read -r yn - if [ "$yn" = "y" ] || [ "$yn" = "Y" ]; then - tar xzf "$meta_dir/etc-backup.tar.gz" -C / - green "/etc restored." - fi - fi - ;; - 4) - echo "" - [ -f "$meta_dir/info.txt" ] && { bold "System info:"; cat "$meta_dir/info.txt"; echo ""; } - [ -f "$meta_dir/flatpak-apps.txt" ] && { bold "Flatpak apps:"; cat "$meta_dir/flatpak-apps.txt"; echo ""; } - [ -f "$meta_dir/layered-packages.txt" ] && { bold "Layered packages:"; cat "$meta_dir/layered-packages.txt"; echo ""; } - [ -f "$meta_dir/rpm-ostree-status.txt" ] && { bold "rpm-ostree status:"; cat "$meta_dir/rpm-ostree-status.txt"; echo ""; } - ;; - b|B) return ;; - esac -} - -# ═══════════════════════════════════════════ -# Main Menu -# ═══════════════════════════════════════════ -while true; do - list_snapshots - - bold "Actions:" - echo " 1) Browse snapshot (mount read-only, copy files manually)" - echo " 2) Restore specific files/directories" - echo " 3) Full home restore (replace entire /var/home)" - echo " 4) Restore system metadata (flatpaks, packages, /etc)" - echo " q) Quit" - echo "" - echo -n "Choice: " - read -r action - - case "$action" in - q|Q) - echo "Closing backup drive..." - umount "$BACKUP_MOUNT" 2>/dev/null || true - cryptsetup luksClose "$LUKS_NAME" 2>/dev/null || true - green "Done." - exit 0 - ;; - 1|2|3|4) - echo -n "Snapshot number: " - read -r num - if [[ ! "$num" =~ ^[0-9]+$ ]] || [ "$num" -lt 1 ] || [ "$num" -gt "${#SNAPSHOTS[@]}" ]; then - red "Invalid selection." - continue - fi - SELECTED="${SNAPSHOTS[$((num-1))]}" - SELECTED_NAME=$(basename "$SELECTED") - - show_metadata "$SELECTED_NAME" - - case "$action" in - 1) browse_snapshot "$SELECTED" ;; - 2) restore_files "$SELECTED" ;; - 3) full_restore "$SELECTED" ;; - 4) restore_metadata "$SELECTED_NAME" ;; - esac - ;; - *) - red "Invalid choice." - ;; - esac -done diff --git a/aurora/setup.sh b/aurora/setup.sh deleted file mode 100755 index 438ea41..0000000 --- a/aurora/setup.sh +++ /dev/null @@ -1,205 +0,0 @@ -#!/bin/bash -# ============================================================================= -# Aurora Machine Setup — Backup Drive + Syncthing -# Run after a fresh Aurora install or home restore -# Usage: sudo bash setup.sh -# ============================================================================= -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -bold() { echo -e "\033[1m$*\033[0m"; } -green() { echo -e "\033[32m$*\033[0m"; } -yellow(){ echo -e "\033[33m$*\033[0m"; } -red() { echo -e "\033[31m$*\033[0m"; } - -die() { red "ERROR: $*"; exit 1; } - -[ "$(id -u)" -eq 0 ] || die "Run with sudo: sudo bash $0" - -REAL_USER="${SUDO_USER:-joey}" -REAL_HOME=$(eval echo "~$REAL_USER") - -echo "" -bold "=========================================" -bold " Aurora Machine Setup" -bold "=========================================" -echo "" - -# ============================================================================= -# 1. BACKUP DRIVE SETUP -# ============================================================================= -bold "[1/2] Backup Drive Setup" -echo "─────────────────────────────────────────" - -# Install backup + restore scripts -cp "$SCRIPT_DIR/backup-to-portable" /usr/local/bin/backup-to-portable -cp "$SCRIPT_DIR/restore-from-portable" /usr/local/bin/restore-from-portable -chmod 755 /usr/local/bin/backup-to-portable /usr/local/bin/restore-from-portable -green " ✓ Installed backup/restore scripts to /usr/local/bin/" - -# Install systemd service -cp "$SCRIPT_DIR/backup-portable.service" /etc/systemd/system/backup-portable.service -systemctl daemon-reload -systemctl enable backup-portable.service -green " ✓ Installed and enabled backup-portable.service" - -# Install udev rule -cp "$SCRIPT_DIR/99-backup-portable.rules" /etc/udev/rules.d/99-backup-portable.rules -udevadm control --reload-rules -green " ✓ Installed udev auto-backup rule" - -# Generate LUKS keyfile if not present -if [ ! -f /etc/backup-drive.key ]; then - dd if=/dev/urandom of=/etc/backup-drive.key bs=4096 count=1 2>/dev/null - chmod 400 /etc/backup-drive.key - green " ✓ Generated new LUKS keyfile at /etc/backup-drive.key" - echo "" - yellow " ⚠ New keyfile generated — you need to add it to the backup drive:" - echo " 1. Plug in the backup drive" - echo " 2. sudo cryptsetup luksAddKey /dev/sdX1 /etc/backup-drive.key" - echo " (Enter your existing passphrase when prompted)" - echo "" -else - green " ✓ LUKS keyfile already exists at /etc/backup-drive.key" -fi - -# Create snapshots directory on source BTRFS -BTRFS_DEVICE=$(findmnt -n -o SOURCE /var/home | sed 's/\[.*//') -if [ -n "$BTRFS_DEVICE" ]; then - mkdir -p /mnt/btrfs-root - mount -o subvolid=5 "$BTRFS_DEVICE" /mnt/btrfs-root 2>/dev/null || true - if mountpoint -q /mnt/btrfs-root; then - mkdir -p /mnt/btrfs-root/snapshots - green " ✓ Snapshot directory ready on source BTRFS" - umount /mnt/btrfs-root - fi -fi - -# Create BTRFS subvolumes for excluded paths (if not already subvolumes) -create_exclude_subvol() { - local path="$1" - local full_path="$REAL_HOME/$path" - - # Skip if path doesn't exist or is already a subvolume - [ -d "$full_path" ] || return 0 - if btrfs subvolume show "$full_path" &>/dev/null; then - green " ✓ $path already a BTRFS subvolume (excluded from snapshots)" - return 0 - fi - - yellow " Converting $path to BTRFS subvolume (excludes from backup snapshots)..." - mv "$full_path" "${full_path}.setup-bak" - btrfs subvolume create "$full_path" - cp -a "${full_path}.setup-bak/." "$full_path/" 2>/dev/null || true - chown "$REAL_USER:$REAL_USER" "$full_path" - rm -rf "${full_path}.setup-bak" - green " ✓ $path → BTRFS subvolume" -} - -create_exclude_subvol ".cache" -create_exclude_subvol ".local/share/containers" - -echo "" - -# ============================================================================= -# 2. SYNCTHING SETUP -# ============================================================================= -bold "[2/2] Syncthing Setup" -echo "─────────────────────────────────────────" - -HPSERVER_ID="5JLI4YY-6TKV3VM-ZJ6J3HT-J3P5UBX-ZCO7OUN-REF4O2P-XHZPI6B-4SNFAAU" - -# Install .stignore -cp "$SCRIPT_DIR/.stignore" "$REAL_HOME/.stignore" -chown "$REAL_USER:$REAL_USER" "$REAL_HOME/.stignore" -green " ✓ Installed .stignore" - -# Check if syncthing is available -SYNCTHING_BIN="" -if command -v syncthing &>/dev/null; then - SYNCTHING_BIN="syncthing" -elif [ -x /home/linuxbrew/.linuxbrew/bin/syncthing ]; then - SYNCTHING_BIN="/home/linuxbrew/.linuxbrew/bin/syncthing" -fi - -if [ -z "$SYNCTHING_BIN" ]; then - yellow " Syncthing not installed. Installing via brew..." - if command -v brew &>/dev/null; then - sudo -u "$REAL_USER" brew install syncthing - SYNCTHING_BIN="/home/linuxbrew/.linuxbrew/bin/syncthing" - elif sudo -u "$REAL_USER" bash -c 'command -v brew' &>/dev/null; then - sudo -u "$REAL_USER" brew install syncthing - SYNCTHING_BIN="/home/linuxbrew/.linuxbrew/bin/syncthing" - else - yellow " ⚠ Homebrew not found. Install syncthing manually:" - echo " brew install syncthing" - echo " Then re-run this script, or manually:" - echo " brew services start syncthing" - echo " syncthing cli config devices add --device-id $HPSERVER_ID --name hpserver" - echo " syncthing cli config devices $HPSERVER_ID addresses add tcp://vgm.joeypayne.com:22000" - echo " syncthing cli config folders add --id home-sync --label Home --path ~/" - echo " syncthing cli config folders home-sync devices add --device-id $HPSERVER_ID" - echo "" - bold "Backup setup complete. Syncthing needs manual install." - exit 0 - fi -fi - -green " ✓ Syncthing found: $($SYNCTHING_BIN --version | head -1)" - -# Start syncthing as user if not running -if ! sudo -u "$REAL_USER" systemctl --user is-active syncthing.service &>/dev/null && \ - ! sudo -u "$REAL_USER" systemctl --user is-active homebrew.syncthing.service &>/dev/null; then - yellow " Starting syncthing..." - sudo -u "$REAL_USER" brew services start syncthing 2>/dev/null || \ - sudo -u "$REAL_USER" systemctl --user enable --now syncthing.service 2>/dev/null || \ - yellow " ⚠ Could not auto-start syncthing. Run: brew services start syncthing" - sleep 3 -fi -green " ✓ Syncthing running" - -# Configure: add hpserver device + home folder share -sudo -u "$REAL_USER" "$SYNCTHING_BIN" cli config devices add \ - --device-id "$HPSERVER_ID" --name "hpserver" 2>/dev/null || true - -# Add explicit address so hpserver is reachable from anywhere (not just LAN) -sudo -u "$REAL_USER" "$SYNCTHING_BIN" cli config devices "$HPSERVER_ID" addresses add \ - "tcp://vgm.joeypayne.com:22000" 2>/dev/null || true - -sudo -u "$REAL_USER" "$SYNCTHING_BIN" cli config folders add \ - --id "home-sync" --label "Home" --path "$REAL_HOME" 2>/dev/null || true - -sudo -u "$REAL_USER" "$SYNCTHING_BIN" cli config folders "home-sync" devices add \ - --device-id "$HPSERVER_ID" 2>/dev/null || true - -green " ✓ Syncthing configured: home-sync → hpserver" - -# Get this device's ID for pairing on hpserver -LOCAL_ID=$(sudo -u "$REAL_USER" "$SYNCTHING_BIN" cli config devices list 2>/dev/null | head -1) -echo "" -yellow " ⚠ If this is a NEW machine, add it to hpserver:" -echo " ssh hpserver \"syncthing cli config devices add --device-id '$LOCAL_ID' --name 'NEW-MACHINE-NAME'\"" -echo " ssh hpserver \"syncthing cli config folders home-sync devices add --device-id '$LOCAL_ID'\"" - -# ============================================================================= -# DONE -# ============================================================================= -echo "" -bold "=========================================" -green " Setup complete!" -bold "=========================================" -echo "" -echo " Backup:" -echo " • Plug in Seagate Portable → backup runs automatically" -echo " • Notification when safe to unplug" -echo " • Restore: sudo restore-from-portable" -if [ ! -f /etc/backup-drive.key ] || [ "$(stat -c%s /etc/backup-drive.key 2>/dev/null)" = "4096" ]; then - echo " • Remember to add keyfile to drive if this is a new machine" -fi -echo "" -echo " Sync:" -echo " • Syncthing syncs home → hpserver whenever online" -echo " • Excludes: caches, containers, downloads, browser data, AI logs" -echo " • Web UI: http://127.0.0.1:8384" -echo "" diff --git a/restore-backup.sh b/restore-backup.sh deleted file mode 100755 index a9357a1..0000000 --- a/restore-backup.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash -# Restore from Portable backup drive on a fresh Aurora install -# Finds the drive, opens LUKS, mounts, and runs the restore script -set -euo pipefail - -bold() { echo -e "\033[1m$*\033[0m"; } -red() { echo -e "\033[31m$*\033[0m"; } -green() { echo -e "\033[32m$*\033[0m"; } - -die() { red "ERROR: $*"; exit 1; } - -[ "$(id -u)" -eq 0 ] || die "Run with sudo: sudo bash restore-backup.sh" - -LUKS_NAME="backup-drive" -MOUNT="/mnt/backup-drive" - -# --- Find the drive --- -# Try stable by-id path first (Seagate Portable), fall back to scanning for LUKS partitions -DEVICE="" -for dev in /dev/disk/by-id/usb-Seagate_Portable_NT3D9HDX-*-part1; do - [ -e "$dev" ] && DEVICE="$dev" && break -done - -if [ -z "$DEVICE" ]; then - bold "Seagate Portable not found by ID. Scanning for LUKS partitions..." - for part in /dev/sd?1 /dev/nvme?n1p?; do - [ -b "$part" ] || continue - if cryptsetup isLuks "$part" 2>/dev/null; then - echo " Found LUKS partition: $part" - DEVICE="$part" - break - fi - done -fi - -[ -n "$DEVICE" ] || die "No backup drive found. Plug it in and try again." -bold "Using device: $DEVICE" - -# --- Open LUKS --- -if [ ! -e "/dev/mapper/$LUKS_NAME" ]; then - echo "" - bold "Enter your backup drive passphrase:" - cryptsetup luksOpen "$DEVICE" "$LUKS_NAME" || die "Failed to unlock drive" -fi -green "LUKS unlocked." - -# --- Mount --- -mkdir -p "$MOUNT" -if ! mountpoint -q "$MOUNT"; then - mount /dev/mapper/"$LUKS_NAME" "$MOUNT" || die "Failed to mount" -fi -green "Mounted at $MOUNT" - -# --- Verify it's a backup drive --- -if [ ! -d "$MOUNT/backups" ] || [ ! -f "$MOUNT/restore.sh" ]; then - die "Drive doesn't look like a backup drive (missing backups/ or restore.sh)" -fi - -echo "" -green "Backup drive ready." -bold "Launching restore menu..." -echo "" - -# --- Run restore --- -bash "$MOUNT/restore.sh"