stemedb/scripts/backup-stemedb.sh
jml 3b5f88b4f0 feat(aphoria): implement claims architecture (A1-A5) with verify engine, corpus, coverage, and explain
Complete Aphoria claims system overhaul:
- A1: Rename ExtractedClaim to Observation (extractors produce observations, not claims)
- A2: Add AuthoredClaim with full provenance, invariants, and authority tiers
- A3: Verify engine comparing observations against authored claims, CLI + formatters
- A4: Corpus as first-class assertions with predicate indexing, authority lens, trust packs
- A5: Coverage analysis, explain/docs generation, self-audit extractor, claim suggester skill

Also includes: 42 extractors updated for Observation type, verifiable_predicates trait,
conflict detection with comparison modes, claims TOML persistence, Grafana dashboard,
backup/restore scripts, and comprehensive test coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 09:11:47 +00:00

185 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# StemeDB Backup Script
#
# Creates a timestamped backup of WAL and database files.
#
# Usage:
# ./scripts/backup-stemedb.sh # Default backup to backups/
# ./scripts/backup-stemedb.sh --output /mnt/nfs # Custom output directory
# ./scripts/backup-stemedb.sh --wal-only # Backup WAL only (faster)
#
# Exit codes:
# 0 - Backup completed successfully
# 1 - Backup failed
#
set -euo pipefail
# Configuration
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
readonly WAL_DIR="${STEMEDB_WAL_DIR:-${PROJECT_DIR}/data/wal}"
readonly DB_DIR="${STEMEDB_DB_DIR:-${PROJECT_DIR}/data/db}"
readonly TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
# Colors (if terminal supports it)
if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'
else
RED=''
GREEN=''
YELLOW=''
BLUE=''
NC=''
fi
# Logging helpers
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
fail() { echo -e "${RED}[FAIL]${NC} $*"; exit 1; }
# Defaults
OUTPUT_DIR="${PROJECT_DIR}/backups"
WAL_ONLY=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--output)
OUTPUT_DIR="$2"
shift 2
;;
--wal-only)
WAL_ONLY=true
shift
;;
--help|-h)
echo "Usage: $0 [--output <dir>] [--wal-only]"
echo ""
echo "Create a timestamped backup of StemeDB data."
echo ""
echo "Options:"
echo " --output <dir> Output directory (default: backups/)"
echo " --wal-only Backup WAL directory only (skip DB)"
echo " --help Show this help message"
echo ""
echo "Environment:"
echo " STEMEDB_WAL_DIR WAL directory (default: data/wal)"
echo " STEMEDB_DB_DIR Database directory (default: data/db)"
exit 0
;;
*)
fail "Unknown argument: $1 (use --help for usage)"
;;
esac
done
readonly BACKUP_DIR="${OUTPUT_DIR}/stemedb-backup-${TIMESTAMP}"
# Cleanup partial backup on failure
cleanup() {
local exit_code=$?
if [[ $exit_code -ne 0 && -d "$BACKUP_DIR" ]]; then
warn "Backup failed, removing partial backup at ${BACKUP_DIR}"
rm -rf "$BACKUP_DIR"
fi
}
trap cleanup EXIT
main() {
echo ""
echo "=========================================="
echo " StemeDB Backup"
echo "=========================================="
echo ""
# Validate source directories
if [[ ! -d "$WAL_DIR" ]]; then
fail "WAL directory not found: ${WAL_DIR}"
fi
if [[ -z "$(ls -A "$WAL_DIR" 2>/dev/null)" ]]; then
fail "WAL directory is empty: ${WAL_DIR}"
fi
if [[ "$WAL_ONLY" == "false" ]]; then
if [[ ! -d "$DB_DIR" ]]; then
fail "DB directory not found: ${DB_DIR}"
fi
if [[ -z "$(ls -A "$DB_DIR" 2>/dev/null)" ]]; then
fail "DB directory is empty: ${DB_DIR}"
fi
fi
# Create backup directory
mkdir -p "$BACKUP_DIR"
info "Backup directory: ${BACKUP_DIR}"
# Backup WAL (append-only, safe to copy live)
info "Copying WAL directory..."
rsync -a "${WAL_DIR}/" "${BACKUP_DIR}/wal/"
local wal_files
wal_files=$(find "${BACKUP_DIR}/wal" -type f | wc -l)
local wal_size
wal_size=$(du -sh "${BACKUP_DIR}/wal" | cut -f1)
success "WAL: ${wal_files} files, ${wal_size}"
# Backup DB (unless --wal-only)
local db_files=0
local db_size="0"
if [[ "$WAL_ONLY" == "false" ]]; then
info "Copying DB directory..."
rsync -a "${DB_DIR}/" "${BACKUP_DIR}/db/"
db_files=$(find "${BACKUP_DIR}/db" -type f | wc -l)
db_size=$(du -sh "${BACKUP_DIR}/db" | cut -f1)
success "DB: ${db_files} files, ${db_size}"
else
info "Skipping DB (--wal-only)"
fi
# Compute total size
local total_size
total_size=$(du -sh "$BACKUP_DIR" | cut -f1)
# Write metadata
cat > "${BACKUP_DIR}/backup-metadata.json" <<METADATA
{
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"source_wal_dir": "${WAL_DIR}",
"source_db_dir": "${DB_DIR}",
"wal_only": ${WAL_ONLY},
"wal_file_count": ${wal_files},
"db_file_count": ${db_files},
"total_size": "${total_size}",
"hostname": "$(hostname)",
"stemedb_version": "$(cargo metadata --format-version=1 --no-deps 2>/dev/null | grep -o '"stemedb-api","version":"[^"]*"' | head -1 | cut -d'"' -f6 || echo "unknown")"
}
METADATA
success "Metadata written"
# Summary
echo ""
echo "=========================================="
echo -e " ${GREEN}Backup complete${NC}"
echo "=========================================="
echo ""
echo " Location: ${BACKUP_DIR}"
echo " WAL files: ${wal_files} (${wal_size})"
if [[ "$WAL_ONLY" == "false" ]]; then
echo " DB files: ${db_files} (${db_size})"
fi
echo " Total: ${total_size}"
echo ""
echo "Restore with:"
echo " ./scripts/restore-stemedb.sh ${BACKUP_DIR}"
echo ""
}
main "$@"