#!/bin/bash # Backup NexusOS to the router. All modes rsync into the canonical nexus-core # folder over the normal SSH daemon (host: router, port 9001) — the SAME folder # restore.sh pulls from, so backup and restore stay in sync. # # backup.sh Fast incremental backup. # backup.sh full Same, but first snapshots the live desktop wiring # and retains runtime/ (minus logs) for a fuller copy. # backup.sh -c | --claude Dry-run: show what a backup WOULD push (content- # level), change nothing. For deciding whether an # actual backup is needed. NEXUS_ROOT="$HOME/nexus-core" DEST="router:/tmp/mnt/Wingdrive2/nexus-core/" mode="$1" # Standard (incremental) excludes — shared by the default backup AND the # -c/--claude check, so the dry-run accurately previews a real backup. EXCLUDES=( --exclude='.git/' --exclude='Promethean/' --exclude='models/blobs/' --exclude='ollama/' --exclude='interface/web/node_modules/' --exclude='interface/web/dist/' --exclude='runtime/' --exclude='__pycache__/' --exclude='*.pyc' ) # Check mode (-c|--claude|check): content-level dry-run, no transfer. Lists what # a standard backup would push to the router (and delete there). --checksum # compares by content (not size/mtime), so only genuine differences show. if [ "$mode" = "-c" ] || [ "$mode" = "--claude" ] || [ "$mode" = "check" ]; then echo "Checking backup diff vs router (dry-run, content-level, no changes)..." rsync -rlni --checksum --delete "${EXCLUDES[@]}" -e ssh "$NEXUS_ROOT/" "$DEST" echo "" echo "(dry-run only — nothing pushed. Apply with: backup or backup full)" exit 0 fi if [ "$mode" = "full" ] || [ "$mode" = "-f" ]; then # Snapshot the live NexusOS desktop wiring into the repo before backing up, # so the backup captures the ACTUAL current state (not just the canonical # defaults baked into assets/themes/install-theme.sh). This is a reference # copy for recovery/diffing; install-theme.sh remains the applier on restore. snap="$NEXUS_ROOT/assets/themes/restore-snapshot" mkdir -p "$snap" { echo "# NexusOS live desktop wiring — snapshot $(date -Is)" echo "# Reference only. Restore is done by assets/themes/install-theme.sh" for p in /Net/ThemeName /Net/IconThemeName /Gtk/CursorThemeName \ /Gtk/CursorThemeSize /Gtk/FontName; do echo "xsettings $p = $(xfconf-query -c xsettings -p "$p" 2>/dev/null)" done echo "xfwm4 /general/theme = $(xfconf-query -c xfwm4 -p /general/theme 2>/dev/null)" } > "$snap/xfconf.txt" 2>/dev/null || true cp -f "$HOME/.config/gtk-3.0/settings.ini" "$snap/gtk-3.0-settings.ini" 2>/dev/null || true cp -f "$HOME/.config/gtk-4.0/settings.ini" "$snap/gtk-4.0-settings.ini" 2>/dev/null || true cp -f "$HOME/.config/xfce4/panel/genmon-13.rc" \ "$NEXUS_ROOT/management/panel/genmon-13.rc" 2>/dev/null || true # Symlink the Claude memory notes (machine knowledge that lives outside the # repo under ~/.claude) into assets/notes/ so the backup below captures them. # rsync runs with -L (copy-links), so the links resolve to real content on # the backup target. Relative links + a loop so new notes are picked up too. # Some of these notes aren't Nexus-specific (Fusion 360, distro/Wine). notes_src="$HOME/.claude/projects/-home-jon-nexus-core/memory" notes_dst="$NEXUS_ROOT/assets/notes" mkdir -p "$notes_dst" for f in "$notes_src"/*.md; do [ -e "$f" ] || continue ln -sfr "$f" "$notes_dst/$(basename "$f")" done rsync -avzL --no-owner --no-group --delete \ --exclude='.git/' \ --exclude='Promethean/' \ --exclude='models/blobs/' \ --exclude='ollama/' \ --exclude='interface/web/node_modules/' \ --exclude='interface/web/dist/' \ --exclude='runtime/logs/' \ --exclude='runtime/backend.log' \ --exclude='runtime/frontend.log' \ --exclude='__pycache__/' \ --exclude='*.pyc' \ -e ssh \ "$NEXUS_ROOT/" "$DEST" else rsync -avz --no-owner --no-group --delete "${EXCLUDES[@]}" -e ssh "$NEXUS_ROOT/" "$DEST" fi