From 64744d1d93c4cbd13cbed8f5469a3a64c32e7207 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Apr 2026 04:43:04 +0000 Subject: [PATCH] Make backup script role-aware and add manual-backup docs Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/a9c69b4d-1c8d-4ade-b444-33043e52fc63 Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com> --- .../scripts/sovran-hub-backup.sh | 83 +++++++++++++++-- docs/manual-backup.md | 93 +++++++++++++++++++ 2 files changed, 166 insertions(+), 10 deletions(-) create mode 100644 docs/manual-backup.md diff --git a/app/sovran_systemsos_web/scripts/sovran-hub-backup.sh b/app/sovran_systemsos_web/scripts/sovran-hub-backup.sh index e4500ce..1a7ce8b 100755 --- a/app/sovran_systemsos_web/scripts/sovran-hub-backup.sh +++ b/app/sovran_systemsos_web/scripts/sovran-hub-backup.sh @@ -18,6 +18,8 @@ BACKUP_LOG="/var/log/sovran-hub-backup.log" BACKUP_STATUS="/var/log/sovran-hub-backup.status" MEDIA_ROOT="/run/media" MIN_FREE_GB=10 +HUB_CONFIG_JSON="/var/lib/sovran-hub/config.json" +ROLE_STATE_NIX="/etc/nixos/role-state.nix" # ── Internal drive labels/paths to NEVER use as backup targets ─── INTERNAL_LABELS=("BTCEcoandBackup" "sovran_systemsos") @@ -125,6 +127,40 @@ for d in flatten(data.get('blockdevices', [])): echo "$target" } +# ── Detect the configured system role ─────────────────────────── +# +# Priority: +# 1. Hub config JSON (/var/lib/sovran-hub/config.json) — "role" key +# 2. role-state.nix (/etc/nixos/role-state.nix) — grep for true flag +# 3. Default: server_plus_desktop + +detect_role() { + local role="server_plus_desktop" + + # 1. Try the Hub config JSON + if [[ -f "$HUB_CONFIG_JSON" ]] && command -v python3 &>/dev/null; then + local r + r=$(python3 -c \ + "import json,sys; d=json.load(open(sys.argv[1])); print(d.get('role',''))" \ + "$HUB_CONFIG_JSON" 2>/dev/null || true) + if [[ -n "$r" ]]; then + echo "$r" + return + fi + fi + + # 2. Fall back to parsing role-state.nix + if [[ -f "$ROLE_STATE_NIX" ]]; then + if grep -q 'roles\.desktop = lib\.mkDefault true' "$ROLE_STATE_NIX" 2>/dev/null; then + role="desktop" + elif grep -q 'roles\.node = lib\.mkDefault true' "$ROLE_STATE_NIX" 2>/dev/null; then + role="node" + fi + fi + + echo "$role" +} + # ── Initialise log file ────────────────────────────────────────── : > "$BACKUP_LOG" @@ -133,6 +169,17 @@ set_status "RUNNING" log "=== Sovran_SystemsOS External Hub Backup ===" log "Starting backup process…" +# ── Detect system role ─────────────────────────────────────────── + +ROLE="$(detect_role)" +case "$ROLE" in + desktop) ROLE_LABEL="Desktop Only" ;; + node) ROLE_LABEL="Node (Bitcoin-only)" ;; + server_plus_desktop) ROLE_LABEL="Server + Desktop" ;; + *) ROLE_LABEL="$ROLE" ;; +esac +log "Detected role: $ROLE_LABEL" + # ── Detect target drive ────────────────────────────────────────── if [[ -n "${BACKUP_TARGET:-}" ]]; then @@ -190,16 +237,29 @@ log "" log "── Stage 2/4: Secrets ───────────────────────────────────────" mkdir -p "$BACKUP_DIR/secrets" -for SRC in /etc/nix-bitcoin-secrets /var/lib/domains; do - if [[ -e "$SRC" ]]; then - rsync -a --info=progress2 "$SRC" "$BACKUP_DIR/secrets/" 2>&1 | tee -a "$BACKUP_LOG" || \ - log "WARNING: Could not copy $SRC — continuing." - else - log " (not found: $SRC — skipping)" - fi -done +if [[ "$ROLE" == "desktop" ]]; then + log "Skipping /etc/nix-bitcoin-secrets — not applicable for Desktop Only role." + # /var/lib/domains is still backed up if present (hub state) + for SRC in /var/lib/domains; do + if [[ -e "$SRC" ]]; then + rsync -a --info=progress2 "$SRC" "$BACKUP_DIR/secrets/" 2>&1 | tee -a "$BACKUP_LOG" || \ + log "WARNING: Could not copy $SRC — continuing." + else + log " (not found: $SRC — skipping)" + fi + done +else + for SRC in /etc/nix-bitcoin-secrets /var/lib/domains; do + if [[ -e "$SRC" ]]; then + rsync -a --info=progress2 "$SRC" "$BACKUP_DIR/secrets/" 2>&1 | tee -a "$BACKUP_LOG" || \ + log "WARNING: Could not copy $SRC — continuing." + else + log " (not found: $SRC — skipping)" + fi + done +fi -# Hub state files from /var/lib/secrets/ +# Hub state files from /var/lib/secrets/ (backed up for all roles) if [[ -d /var/lib/secrets ]]; then mkdir -p "$BACKUP_DIR/secrets/hub-state" rsync -a --info=progress2 /var/lib/secrets/ "$BACKUP_DIR/secrets/hub-state/" 2>&1 | tee -a "$BACKUP_LOG" || \ @@ -230,7 +290,9 @@ fi log "" log "── Stage 4/4: Wallet and node data (/var/lib/lnd) ──────────" -if [[ -d /var/lib/lnd ]]; then +if [[ "$ROLE" == "desktop" ]]; then + log "Skipping Stage 4 (LND wallet data) — not applicable for Desktop Only role." +elif [[ -d /var/lib/lnd ]]; then rsync -a --info=progress2 \ --exclude='logs/' \ /var/lib/lnd/ "$BACKUP_DIR/lnd/" 2>&1 | tee -a "$BACKUP_LOG" || \ @@ -248,6 +310,7 @@ log "Generating BACKUP_MANIFEST.txt …" echo "Sovran_SystemsOS Backup Manifest" echo "Generated: $(date)" echo "Hostname: $(hostname)" + echo "Role: $ROLE_LABEL" echo "Target: $TARGET" echo "" echo "Contents:" diff --git a/docs/manual-backup.md b/docs/manual-backup.md new file mode 100644 index 0000000..aa6268d --- /dev/null +++ b/docs/manual-backup.md @@ -0,0 +1,93 @@ +# Sovran Hub — Manual Backup + +The manual backup service copies critical system data from your Sovran Pro to an external USB drive, providing a third copy of your data (your Sovran Pro already maintains an automatic internal backup on its second drive). + +Backups are written to: + +``` +/Sovran_SystemsOS_Backup// +``` + +where `` is formatted as `YYYYMMDD_HHMMSS`. + +--- + +## Backup Stages + +The script always attempts all four stages, but skips stages that are irrelevant to the system's configured role (see [Per-Role Breakdown](#per-role-breakdown) below). + +| Stage | Directory | Contents | +|-------|-----------|----------| +| **1/4 — NixOS config** | `/etc/nixos/` | Full NixOS system configuration: `role-state.nix`, `custom.nix`, flake files, and any other config managed by the Hub | +| **2/4 — Secrets** | `/etc/nix-bitcoin-secrets`, `/var/lib/domains`, `/var/lib/secrets` | Bitcoin/LND secrets, domain configurations for all web services, and Hub state files | +| **3/4 — Home directory** | `/home/` | All user home directories (`.cache/` and Trash are excluded) | +| **4/4 — LND wallet data** | `/var/lib/lnd/` | Lightning Network node wallet and channel data (log files excluded) | + +--- + +## Per-Role Breakdown + +The script detects the system role at runtime by reading `/var/lib/sovran-hub/config.json` (falling back to `/etc/nixos/role-state.nix`) and adjusts its behaviour accordingly. + +### Server + Desktop (default) + +All services are enabled: Bitcoin, Matrix Synapse, Vaultwarden, WordPress, Nextcloud. + +| Stage | Status | Notes | +|-------|--------|-------| +| Stage 1 — NixOS config | ✅ Backed up | Full server configuration | +| Stage 2 — Secrets | ✅ Backed up | Bitcoin secrets, domain configs, and Hub state | +| Stage 3 — Home directory | ✅ Backed up | Desktop user data | +| Stage 4 — LND wallet | ✅ Backed up | Lightning wallet and channel data | + +This produces the largest backup. All four stages generate meaningful data. + +### Desktop Only + +All server services are disabled (`bitcoin = false`, `synapse = false`, `vaultwarden = false`, `wordpress = false`, `nextcloud = false`). Only GNOME desktop is active. + +| Stage | Status | Notes | +|-------|--------|-------| +| Stage 1 — NixOS config | ✅ Backed up | Simpler config (no server services) | +| Stage 2 — Secrets | ⚠️ Partial | `/etc/nix-bitcoin-secrets` is **skipped** (not applicable for Desktop Only role). `/var/lib/domains` and `/var/lib/secrets` (Hub state) are still backed up if present | +| Stage 3 — Home directory | ✅ Backed up | **The most important data for this role** | +| Stage 4 — LND wallet | ⏭️ Skipped | Explicitly skipped — not applicable for Desktop Only role | + +This produces the smallest and fastest backup. Stages 1 and 3 are the primary sources of meaningful data. + +### Node (Bitcoin-only) + +Only the Bitcoin ecosystem is active: `bitcoind`, `electrs`, `lnd`, `rtl`, `btcpay`, `mempool`, and `bip110`. All other server services are disabled. + +| Stage | Status | Notes | +|-------|--------|-------| +| Stage 1 — NixOS config | ✅ Backed up | Node-specific configuration | +| Stage 2 — Secrets | ✅ Backed up | Bitcoin secrets and Hub state. `/var/lib/domains` may be minimal (BTCPay runs but is not exposed via Caddy) | +| Stage 3 — Home directory | ✅ Backed up | User data | +| Stage 4 — LND wallet | ✅ Backed up | **Critical** — Lightning wallet and channel data | + +All four stages run, matching Server + Desktop behaviour. The `/var/lib/domains` directory may be sparsely populated since non-Bitcoin web services are not configured. + +--- + +## Backup Manifest + +After all stages complete, the script writes a `BACKUP_MANIFEST.txt` file inside the timestamped backup directory. This file records the date, hostname, detected role, target drive, and a directory listing of everything that was backed up. + +--- + +## Running the Backup + +The backup is triggered from the Sovran Hub web UI. You can also run it directly: + +```bash +# Auto-detect the first external USB drive +sudo bash /path/to/sovran-hub-backup.sh + +# Specify a target drive explicitly +sudo BACKUP_TARGET=/run/media// bash /path/to/sovran-hub-backup.sh +``` + +The script requires at least **10 GB** of free space on the target drive and will refuse to write to internal system drives. + +Logs are written to `/var/log/sovran-hub-backup.log` and the current status (`RUNNING`, `SUCCESS`, or `FAILED`) is tracked in `/var/log/sovran-hub-backup.status`.