backup: 2026-07-11T07:54:39-05:00

This commit is contained in:
jon
2026-07-11 07:54:39 -05:00
parent 8451662d9c
commit 86a26233dc
4 changed files with 466 additions and 97 deletions
-4
View File
@@ -17,10 +17,6 @@ synapse/memory/memory.db-shm
# Ollama model blobs (~15G, regenerable via `ollama pull`) # Ollama model blobs (~15G, regenerable via `ollama pull`)
models/ models/
# runtime user data
data/uploads/
data/exports/
# local editor / assistant tooling # local editor / assistant tooling
.vscode/ .vscode/
.claude/ .claude/
+67 -63
View File
@@ -1,50 +1,41 @@
#!/bin/bash #!/bin/bash
# Backup NexusOS to the router. All modes rsync into the canonical nexus-core # Backup NexusOS via git → Gitea (enderofwings/NexusOS). Replaces the old
# folder over the normal SSH daemon (host: router, port 9001) — the SAME folder # rsync-to-router backup. Commits the tracked tree (source, config, playbooks,
# restore.sh pulls from, so backup and restore stay in sync. # branding, docs) plus a SQL dump of the memory DB, then pushes to the remote —
# so a fresh clone + restore.sh fully rebuilds the machine.
# #
# backup.sh Fast incremental backup. # backup.sh Commit changed files + push.
# backup.sh full Same, but first snapshots the live desktop wiring # backup.sh full | -f Also snapshot live desktop wiring + Claude notes first.
# and retains runtime/ (minus logs) for a fuller copy. # backup.sh -c | --claude Dry-run: show what a backup WOULD commit/push. No changes.
# backup.sh -c | --claude Dry-run: show what a backup WOULD push (content- #
# level), change nothing. For deciding whether an # The regenerable heavies (Promethean venv, ollama binary, models/, node_modules,
# actual backup is needed. # dist, runtime logs) are intentionally NOT backed up — restore.sh rebuilds them
# from requirements-*.txt / package-lock.json / `ollama pull`.
NEXUS_ROOT="$HOME/nexus-core" NEXUS_ROOT="$HOME/nexus-core"
DEST="router:/tmp/mnt/Wingdrive2/nexus-core/" cd "$NEXUS_ROOT" || { echo "No $NEXUS_ROOT"; exit 1; }
mode="$1" mode="${1:-}"
# Standard (incremental) excludes — shared by the default backup AND the DB="synapse/memory/memory.db"
# -c/--claude check, so the dry-run accurately previews a real backup. DB_SQL="synapse/memory/memory.db.sql"
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 # Dump the (gitignored, binary, WAL) memory DB to a diff-friendly SQL file so git
# a standard backup would push to the router (and delete there). --checksum # backs up the assistant's memory + conversation history. sqlite3 reads through
# compares by content (not size/mtime), so only genuine differences show. # the WAL, so the dump includes the latest committed data without a checkpoint.
if [ "$mode" = "-c" ] || [ "$mode" = "--claude" ] || [ "$mode" = "check" ]; then dump_db() {
echo "Checking backup diff vs router (dry-run, content-level, no changes)..." [ -f "$DB" ] || return 0
rsync -rlni --checksum --delete "${EXCLUDES[@]}" -e ssh "$NEXUS_ROOT/" "$DEST" if sqlite3 "$DB" ".dump" > "$DB_SQL" 2>/dev/null; then
echo "" echo "Dumped memory DB → $DB_SQL"
echo "(dry-run only — nothing pushed. Apply with: backup or backup full)" else
exit 0 echo "Warning: could not dump $DB (sqlite3 missing?) — DB not captured"
fi fi
}
if [ "$mode" = "full" ] || [ "$mode" = "-f" ]; then # full mode: snapshot the LIVE desktop wiring into the repo (reference copy for
# Snapshot the live NexusOS desktop wiring into the repo before backing up, # recovery/diffing; install-theme.sh remains the applier on restore), and copy
# so the backup captures the ACTUAL current state (not just the canonical # the Claude memory notes in so git commits their content.
# defaults baked into assets/themes/install-theme.sh). This is a reference snapshot_desktop() {
# copy for recovery/diffing; install-theme.sh remains the applier on restore.
snap="$NEXUS_ROOT/assets/themes/restore-snapshot" snap="$NEXUS_ROOT/assets/themes/restore-snapshot"
mkdir -p "$snap" mkdir -p "$snap"
{ {
@@ -61,33 +52,46 @@ if [ "$mode" = "full" ] || [ "$mode" = "-f" ]; then
cp -f "$HOME/.config/xfce4/panel/genmon-13.rc" \ cp -f "$HOME/.config/xfce4/panel/genmon-13.rc" \
"$NEXUS_ROOT/management/panel/genmon-13.rc" 2>/dev/null || true "$NEXUS_ROOT/management/panel/genmon-13.rc" 2>/dev/null || true
# Symlink the Claude memory notes (machine knowledge that lives outside the # Copy the Claude memory notes (machine knowledge under ~/.claude) into the
# repo under ~/.claude) into assets/notes/ so the backup below captures them. # repo so they're versioned too. Some aren't Nexus-specific (Fusion 360, etc.).
# 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_src="$HOME/.claude/projects/-home-jon-nexus-core/memory"
notes_dst="$NEXUS_ROOT/assets/notes" notes_dst="$NEXUS_ROOT/assets/notes"
mkdir -p "$notes_dst" mkdir -p "$notes_dst"
for f in "$notes_src"/*.md; do cp -f "$notes_src"/*.md "$notes_dst/" 2>/dev/null || true
[ -e "$f" ] || continue }
ln -sfr "$f" "$notes_dst/$(basename "$f")"
done
rsync -avzL --no-owner --no-group --delete \ # Check mode: dry-run. Show what a real backup would commit and push.
--exclude='.git/' \ if [ "$mode" = "-c" ] || [ "$mode" = "--claude" ] || [ "$mode" = "check" ]; then
--exclude='Promethean/' \ dump_db
--exclude='models/blobs/' \ echo ""
--exclude='ollama/' \ echo "Files a backup would commit:"
--exclude='interface/web/node_modules/' \ git status --short
--exclude='interface/web/dist/' \ echo ""
--exclude='runtime/logs/' \ echo "Local commits not yet pushed:"
--exclude='runtime/backend.log' \ git log --oneline @{u}.. 2>/dev/null || echo "(none / no upstream)"
--exclude='runtime/frontend.log' \ echo ""
--exclude='__pycache__/' \ echo "(dry-run only — nothing committed or pushed. Apply with: backup)"
--exclude='*.pyc' \ exit 0
-e ssh \ fi
"$NEXUS_ROOT/" "$DEST"
else if [ "$mode" = "full" ] || [ "$mode" = "-f" ]; then
rsync -avz --no-owner --no-group --delete "${EXCLUDES[@]}" -e ssh "$NEXUS_ROOT/" "$DEST" snapshot_desktop
fi
dump_db
git add -A
if git diff --cached --quiet; then
echo "No changes to commit."
else
git commit -q -m "backup: $(date -Is)"
echo "Committed backup snapshot."
fi
echo "Pushing to Gitea..."
if git push origin main; then
echo "Backup complete."
else
echo "Push failed. Set up credentials once with:"
echo " git config credential.helper store # then push once and enter your Gitea token"
exit 1
fi fi
+46 -29
View File
@@ -1,22 +1,18 @@
#!/bin/bash #!/bin/bash
NEXUS_ROOT="$HOME/nexus-core" # Restore NexusOS from Gitea (enderofwings/NexusOS). Clones or pulls the repo,
SRC="router:/tmp/mnt/Wingdrive2/nexus-core/" # rebuilds the memory DB from its SQL dump, then rebuilds the regenerable env
mode="$1" # (venv, frontend deps) and desktop integration (theme, terminal, panel).
#
# restore.sh Pull latest + rebuild.
# restore.sh -c | --claude Dry-run: fetch + show what a restore WOULD apply. No changes.
# Shared exclude list — used by the real restore AND the -c/--claude dry-run, so NEXUS_ROOT="$HOME/nexus-core"
# the check accurately previews what a real restore would do. REPO="https://git.enderofwings.com/enderofwings/NexusOS.git"
EXCLUDES=( mode="${1:-}"
--exclude='.git/'
--exclude='Promethean/' DB="$NEXUS_ROOT/synapse/memory/memory.db"
--exclude='models/blobs/' DB_SQL="$NEXUS_ROOT/synapse/memory/memory.db.sql"
--exclude='ollama/'
--exclude='interface/web/node_modules/'
--exclude='interface/web/dist/'
--exclude='runtime/'
--exclude='__pycache__/'
--exclude='*.pyc'
)
detect_requirements() { detect_requirements() {
if command -v nvidia-smi &>/dev/null || lspci 2>/dev/null | grep -qi nvidia; then if command -v nvidia-smi &>/dev/null || lspci 2>/dev/null | grep -qi nvidia; then
@@ -26,30 +22,49 @@ detect_requirements() {
fi fi
} }
# Check mode (-c|--claude|check): content-level dry-run. Lists exactly what a # Fresh machine: clone. Existing: work in place.
# real restore would pull from the router and delete locally — nothing changes if [ ! -d "$NEXUS_ROOT/.git" ]; then
# and the env/theme rebuild is skipped. --checksum compares by content (not echo "No local repo — cloning from Gitea..."
# size/mtime), so only genuine differences show. Meant for deciding whether a git clone "$REPO" "$NEXUS_ROOT" || { echo "Clone failed."; exit 1; }
# real restore is actually needed before paying for the rebuild. fi
cd "$NEXUS_ROOT" || { echo "No $NEXUS_ROOT"; exit 1; }
# Check mode: dry-run. Fetch and show what a real restore would pull, no changes.
if [ "$mode" = "-c" ] || [ "$mode" = "--claude" ] || [ "$mode" = "check" ]; then if [ "$mode" = "-c" ] || [ "$mode" = "--claude" ] || [ "$mode" = "check" ]; then
echo "Checking restore diff vs router (dry-run, content-level, no changes)..." echo "Fetching to preview restore (no changes)..."
rsync -rlni --checksum --delete "${EXCLUDES[@]}" -e ssh "$SRC" "$NEXUS_ROOT/" git fetch origin
echo "" echo ""
echo "(dry-run only — nothing changed. Apply with: restore -f)" echo "Commits a restore would apply:"
git log --oneline ..origin/main 2>/dev/null || echo "(up to date)"
echo ""
git diff --stat ..origin/main 2>/dev/null
echo ""
echo "(dry-run only — nothing changed. Apply with: restore)"
exit 0 exit 0
fi fi
if [ "$mode" = "full" ] || [ "$mode" = "-f" ]; then echo "Pulling latest from Gitea..."
echo "Restoring full Nexus backup from router..." git pull --ff-only origin main || { echo "Pull failed (diverged? stash/commit local changes)."; exit 1; }
else
echo "Restoring Nexus from router..." # Rebuild the memory DB from its SQL dump — only if there's no live DB, so we
# never clobber a machine that has newer conversations than the backup.
if [ -f "$DB_SQL" ] && [ ! -f "$DB" ]; then
echo ""
echo "Rebuilding memory DB from dump..."
sqlite3 "$DB" < "$DB_SQL" && echo "Memory DB restored." \
|| echo "Warning: memory DB rebuild failed."
elif [ -f "$DB" ]; then
echo "Live memory DB present — left as-is (dump not applied). Delete it first to restore from backup."
fi fi
rsync -avz --no-owner --no-group --delete "${EXCLUDES[@]}" -e ssh "$SRC" "$NEXUS_ROOT/"
echo "" echo ""
echo "Rebuilding Python environment..." echo "Rebuilding Python environment..."
if [ ! -x "$NEXUS_ROOT/Promethean/bin/pip" ]; then
python3 -m venv "$NEXUS_ROOT/Promethean"
fi
req=$(detect_requirements) req=$(detect_requirements)
if [ -f "$NEXUS_ROOT/$req" ]; then if [ -f "$NEXUS_ROOT/$req" ]; then
"$NEXUS_ROOT/Promethean/bin/pip" install --upgrade pip -q
"$NEXUS_ROOT/Promethean/bin/pip" install -r "$NEXUS_ROOT/$req" "$NEXUS_ROOT/Promethean/bin/pip" install -r "$NEXUS_ROOT/$req"
else else
echo "Warning: $req not found — skipping pip install." echo "Warning: $req not found — skipping pip install."
@@ -58,6 +73,7 @@ fi
echo "" echo ""
echo "Rebuilding frontend dependencies..." echo "Rebuilding frontend dependencies..."
cd "$NEXUS_ROOT/interface/web" && npm install cd "$NEXUS_ROOT/interface/web" && npm install
cd "$NEXUS_ROOT"
echo "" echo ""
echo "Restoring NexusOS desktop theme..." echo "Restoring NexusOS desktop theme..."
@@ -90,3 +106,4 @@ fi
echo "" echo ""
echo "Restore complete. Nexus is ready to start." echo "Restore complete. Nexus is ready to start."
echo "Note: Ollama models are not in the backup — pull them with \`ollama pull <model>\` as needed."
File diff suppressed because one or more lines are too long