#!/usr/bin/env bash # # Weekly SkateSpots relay archive + optional offsite copy. # # Driven by the systemd user timer `skatespots-archive.timer` (install with # scripts/install-archive-timer.sh). Local archives land in # ~/.backups/relay; if ~/.config/skatespots/restic.env exists, the whole # directory is pushed to a restic repository (SFTP to our own host, or any # S3/R2/B2 store) and old snapshots are pruned. Without that file the archive is # local-only — still useful, but not a real offsite backup. # # The relay lives on OUR host. The archive reaches it over an SSH tunnel to that # host's 127.0.0.1:7777 (so it never depends on public DNS resolving from this # machine). If the tunnel can't be established it falls back to the public URL. # Override with SKATESPOTS_RELAY_SSH (default: the `skatespots-cvm` ssh alias) # and SKATESPOTS_RELAY_TUNNEL_PORT (default 17777). # # Manual run / debug: # scripts/backup-relay.sh # journalctl --user -u skatespots-archive -n 50 set -euo pipefail REPO="/home/sovereignsk8/SkateSpots" # systemd runs with a minimal PATH; node lives under nvm here. export PATH="/home/sovereignsk8/.nvm/versions/node/v22.23.1/bin:$PATH" ARCHIVE_DIR="${SKATESPOTS_ARCHIVE_DIR:-$HOME/.backups/relay}" RESTIC_ENV="$HOME/.config/skatespots/restic.env" # Optional SECOND offsite (e.g. an R2/B2 bucket) so we get true 3-2-1: local + # CVM host + object storage. Same format as restic.env. RESTIC_ENV_2="$HOME/.config/skatespots/restic-2.env" TUNNEL_PORT="${SKATESPOTS_RELAY_TUNNEL_PORT:-17777}" SSH_TARGET="${SKATESPOTS_RELAY_SSH:-skatespots-cvm}" log() { echo "[$(date -Is)] $*"; } mkdir -p "$ARCHIVE_DIR" cd "$REPO" # Port-open probe (uses node, which is always on PATH here). port_open() { node -e 'const n=require("net");const s=n.connect(+process.argv[1],"127.0.0.1");s.on("connect",()=>{s.destroy();process.exit(0)});s.on("error",()=>process.exit(1));setTimeout(()=>process.exit(1),2000)' "$1" 2>/dev/null } log "archiving the community relay…" RELAY_ARG="" TUNNEL_PID="" ssh -N -o BatchMode=yes -o ExitOnForwardFailure=yes \ -L "127.0.0.1:${TUNNEL_PORT}:127.0.0.1:7777" "$SSH_TARGET" >/dev/null 2>&1 & TUNNEL_PID=$! trap 'kill "$TUNNEL_PID" 2>/dev/null || true' EXIT for _ in 1 2 3 4 5; do port_open "$TUNNEL_PORT" && break sleep 1 done if port_open "$TUNNEL_PORT"; then RELAY_ARG="--relay ws://127.0.0.1:${TUNNEL_PORT}" log " via SSH tunnel to $SSH_TARGET (localhost:7777)" else log " tunnel unavailable — using the public relay URL" fi # shellcheck disable=SC2086 node scripts/archive-relay.mjs $RELAY_ARG # Push the archive directory to every configured restic repo. Each is sourced in # its own subshell so RESTIC_* variables don't leak between destinations. copy_offsite() { local envfile="$1" [ -f "$envfile" ] || return 0 ( set -a; . "$envfile"; set +a if [ -z "${RESTIC_REPOSITORY:-}" ]; then log " $(basename "$envfile"): no RESTIC_REPOSITORY — skipping" exit 0 fi log " → $RESTIC_REPOSITORY" restic backup --tag skatespots-relay "$ARCHIVE_DIR" restic forget --tag skatespots-relay --keep-last 12 --prune ) } if [ -f "$RESTIC_ENV" ] || [ -f "$RESTIC_ENV_2" ]; then log "pushing offsite copies…" copy_offsite "$RESTIC_ENV" copy_offsite "$RESTIC_ENV_2" log "offsite copies done" else log "no $RESTIC_ENV — archive kept local-only (set one up for a real offsite backup)" fi