2026-02-25 23:59:28 +01:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# tools/cmd/railiance-backup — backup custodian state to Nextcloud file drop
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
|
|
|
source "${ROOT}/lib/railiance-print.sh"
|
|
|
|
|
|
|
|
|
|
# ── Configuration ─────────────────────────────────────────────────────────────
|
|
|
|
|
AGE_PUBLIC_KEY="age1zvryunvjhvpkmasskauga2heeg0ztnte9ymgppvjge36ekumk50syr3tsz"
|
2026-07-07 17:16:30 +02:00
|
|
|
BAO_PATH="platform/workloads/railiance/backup/offsite-lane"
|
|
|
|
|
NC_WEBDAV_URL="${RAILIANCE_BACKUP_NC_WEBDAV_URL:-}"
|
|
|
|
|
NC_TOKEN="${RAILIANCE_BACKUP_NC_TOKEN:-}"
|
|
|
|
|
|
|
|
|
|
if [[ -z "${NC_TOKEN}" || -z "${NC_WEBDAV_URL}" ]] && command -v bao >/dev/null 2>&1; then
|
|
|
|
|
if bao kv metadata get "${BAO_PATH}" >/dev/null 2>&1; then
|
|
|
|
|
NC_TOKEN="$(bao kv get -field=NC_WEBDAV_TOKEN "${BAO_PATH}")"
|
|
|
|
|
NC_WEBDAV_URL="$(bao kv get -field=NC_WEBDAV_URL "${BAO_PATH}")"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
: "${NC_WEBDAV_URL:=https://nx4069.your-storageshare.de/public.php/webdav}"
|
|
|
|
|
if [[ -z "${NC_TOKEN}" ]]; then
|
|
|
|
|
bad "credentials" "set RAILIANCE_BACKUP_NC_TOKEN or: bao login -method=oidc -path=netkingdom role=railiance-backup-workload-kv-read"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
2026-02-25 23:59:28 +01:00
|
|
|
PG_CONTAINER="infra-postgres-1"
|
|
|
|
|
PG_USER="custodian"
|
|
|
|
|
PG_DB="custodian"
|
|
|
|
|
BACKUP_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/railiance/backups"
|
|
|
|
|
TS="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
|
|
|
|
|
|
|
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
|
nc_upload() {
|
|
|
|
|
local file="$1" name="$2"
|
|
|
|
|
curl -sf -u "${NC_TOKEN}:" -T "$file" "${NC_WEBDAV_URL}/${name}" \
|
|
|
|
|
|| { bad "upload" "failed to upload ${name}"; exit 1; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# ── Main ──────────────────────────────────────────────────────────────────────
|
|
|
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
|
print_hdr "Railiance Backup — ${TS}"
|
|
|
|
|
|
|
|
|
|
# 1. PostgreSQL
|
|
|
|
|
ok "postgres" "dumping ${PG_DB}…"
|
|
|
|
|
TMP_DB="${BACKUP_DIR}/db-${TS}.sql.age"
|
|
|
|
|
docker exec "${PG_CONTAINER}" pg_dump -U "${PG_USER}" "${PG_DB}" \
|
|
|
|
|
| age -r "${AGE_PUBLIC_KEY}" -o "${TMP_DB}"
|
|
|
|
|
ok "postgres" "encrypted → $(basename "$TMP_DB")"
|
|
|
|
|
nc_upload "${TMP_DB}" "db-${TS}.sql.age"
|
|
|
|
|
ok "postgres" "uploaded to Nextcloud"
|
|
|
|
|
|
|
|
|
|
# 2. Config snapshot
|
|
|
|
|
ok "config" "snapshotting ~/.claude/ ~/.claude.json .gitconfig…"
|
|
|
|
|
TMP_CFG_TAR="${BACKUP_DIR}/config-${TS}.tar.gz"
|
|
|
|
|
TMP_CFG="${BACKUP_DIR}/config-${TS}.tar.gz.age"
|
|
|
|
|
tar -czf "${TMP_CFG_TAR}" \
|
|
|
|
|
-C "$HOME" \
|
|
|
|
|
--ignore-failed-read \
|
|
|
|
|
.claude \
|
|
|
|
|
.claude.json \
|
|
|
|
|
.gitconfig \
|
|
|
|
|
2>/dev/null || true
|
|
|
|
|
age -r "${AGE_PUBLIC_KEY}" -o "${TMP_CFG}" "${TMP_CFG_TAR}"
|
|
|
|
|
rm -f "${TMP_CFG_TAR}"
|
|
|
|
|
ok "config" "encrypted → $(basename "$TMP_CFG")"
|
|
|
|
|
nc_upload "${TMP_CFG}" "config-${TS}.tar.gz.age"
|
|
|
|
|
ok "config" "uploaded to Nextcloud"
|
|
|
|
|
|
|
|
|
|
# 3. Prune local cache (keep last 7 of each type)
|
|
|
|
|
find "${BACKUP_DIR}" -name "db-*.sql.age" | sort -r | tail -n +8 | xargs -r rm -f
|
|
|
|
|
find "${BACKUP_DIR}" -name "config-*.tar.gz.age" | sort -r | tail -n +8 | xargs -r rm -f
|
|
|
|
|
ok "cache" "local copies pruned (keep last 7)"
|
|
|
|
|
|
|
|
|
|
# 4. Write stamp (preflight reads this)
|
|
|
|
|
echo "${TS}" > "${BACKUP_DIR}/.last-backup"
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
ok "done" "Backup complete — ${TS}"
|
|
|
|
|
echo " DB: db-${TS}.sql.age"
|
|
|
|
|
echo " Config: config-${TS}.tar.gz.age"
|
|
|
|
|
echo
|
|
|
|
|
echo " ⚠️ The age private key at ~/.config/age/railiance-backup.key"
|
|
|
|
|
echo " MUST also be stored in your password manager or written down."
|
|
|
|
|
echo " Without it you cannot decrypt these backups."
|