Initial commit: VaultMesh Skills collection
Collection of operational skills for VaultMesh infrastructure including: - backup-sovereign: Backup and recovery operations - btc-anchor: Bitcoin anchoring - cloudflare-tunnel-manager: Cloudflare tunnel management - container-registry: Container registry operations - disaster-recovery: Disaster recovery procedures - dns-sovereign: DNS management - eth-anchor: Ethereum anchoring - gitea-bootstrap: Gitea setup and configuration - hetzner-bootstrap: Hetzner server provisioning - merkle-forest: Merkle tree operations - node-hardening: Node security hardening - operator-bootstrap: Operator initialization - proof-verifier: Cryptographic proof verification - rfc3161-anchor: RFC3161 timestamping - secrets-vault: Secrets management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
136
backup-sovereign/scripts/11_backup_apply.sh
Executable file
136
backup-sovereign/scripts/11_backup_apply.sh
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# === METADATA ===
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# === CONFIGURATION ===
|
||||
: "${NODE_NAME:=node-a}"
|
||||
: "${BACKUP_LABEL:=manual}"
|
||||
: "${OUTPUT_DIR:=$SKILL_ROOT/outputs}"
|
||||
: "${BACKUP_SOURCES:=}"
|
||||
: "${BACKUP_EXCLUDES:=.git,node_modules,target,dist,outputs}"
|
||||
: "${DRY_RUN:=1}"
|
||||
: "${REQUIRE_CONFIRM:=1}"
|
||||
: "${CONFIRM_PHRASE:=I UNDERSTAND THIS WILL CREATE AND ENCRYPT BACKUPS}"
|
||||
|
||||
# === FUNCTIONS ===
|
||||
log_info() { echo "[INFO] $(date -Iseconds) $*"; }
|
||||
log_error() { echo "[ERROR] $(date -Iseconds) $*" >&2; }
|
||||
die() { log_error "$@"; exit 1; }
|
||||
|
||||
b3_file() {
|
||||
if command -v b3sum &>/dev/null; then
|
||||
b3sum "$1" | awk '{print $1}'
|
||||
else
|
||||
blake3 "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
require_confirm() {
|
||||
[[ "$DRY_RUN" == "0" ]] || die "DRY_RUN=$DRY_RUN (set DRY_RUN=0 to apply)."
|
||||
|
||||
if [[ "$REQUIRE_CONFIRM" == "1" ]]; then
|
||||
echo ""
|
||||
echo "CONFIRMATION REQUIRED"
|
||||
echo "Type the phrase exactly to continue:"
|
||||
echo " $CONFIRM_PHRASE"
|
||||
read -r input
|
||||
[[ "$input" == "$CONFIRM_PHRASE" ]] || die "Confirmation phrase mismatch; aborting."
|
||||
fi
|
||||
}
|
||||
|
||||
json_array() {
|
||||
# Convert comma-separated string to JSON array
|
||||
local input="$1"
|
||||
local first=true
|
||||
echo -n "["
|
||||
IFS=',' read -r -a items <<< "$input"
|
||||
for item in "${items[@]}"; do
|
||||
if [[ "$first" == "true" ]]; then
|
||||
first=false
|
||||
else
|
||||
echo -n ","
|
||||
fi
|
||||
echo -n "\"$item\""
|
||||
done
|
||||
echo -n "]"
|
||||
}
|
||||
|
||||
main() {
|
||||
[[ -n "$BACKUP_SOURCES" ]] || die "BACKUP_SOURCES is required (comma-separated paths)."
|
||||
|
||||
require_confirm
|
||||
|
||||
local ts run_id run_dir archive excludes_file manifest
|
||||
ts="$(date -Iseconds | tr ':' '-')"
|
||||
run_id="${NODE_NAME}_${BACKUP_LABEL}_${ts}"
|
||||
run_dir="$OUTPUT_DIR/runs/$run_id"
|
||||
archive="$run_dir/archive.tar.gz"
|
||||
excludes_file="$run_dir/excludes.txt"
|
||||
manifest="$run_dir/manifest.json"
|
||||
|
||||
mkdir -p "$run_dir"
|
||||
|
||||
# Write excludes file
|
||||
log_info "Writing excludes: $excludes_file"
|
||||
: > "$excludes_file"
|
||||
IFS=',' read -r -a excludes <<< "$BACKUP_EXCLUDES"
|
||||
for ex in "${excludes[@]}"; do
|
||||
echo "$ex" >> "$excludes_file"
|
||||
done
|
||||
|
||||
# Build tar exclude args
|
||||
local tar_excludes=()
|
||||
while IFS= read -r pat; do
|
||||
[[ -n "$pat" ]] && tar_excludes+=("--exclude=$pat")
|
||||
done < "$excludes_file"
|
||||
|
||||
# Expand sources
|
||||
local expanded_sources=()
|
||||
IFS=',' read -r -a sources <<< "$BACKUP_SOURCES"
|
||||
for src in "${sources[@]}"; do
|
||||
expanded_sources+=("${src/#\~/$HOME}")
|
||||
done
|
||||
|
||||
# Create archive
|
||||
log_info "Creating archive: $archive"
|
||||
tar -czf "$archive" "${tar_excludes[@]}" "${expanded_sources[@]}"
|
||||
|
||||
local archive_size archive_b3
|
||||
archive_size=$(stat -c%s "$archive")
|
||||
archive_b3=$(b3_file "$archive")
|
||||
|
||||
log_info "Archive size: $archive_size bytes"
|
||||
log_info "Archive BLAKE3: $archive_b3"
|
||||
|
||||
# Create manifest (pure bash JSON)
|
||||
log_info "Writing manifest: $manifest"
|
||||
cat > "$manifest" <<EOF
|
||||
{
|
||||
"version": 1,
|
||||
"node": "$NODE_NAME",
|
||||
"label": "$BACKUP_LABEL",
|
||||
"run_id": "$run_id",
|
||||
"created_at": "$(date -Iseconds)",
|
||||
"sources": $(json_array "$BACKUP_SOURCES"),
|
||||
"excludes": $(json_array "$BACKUP_EXCLUDES"),
|
||||
"archive": {
|
||||
"path": "archive.tar.gz",
|
||||
"bytes": $archive_size,
|
||||
"blake3": "$archive_b3"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Save last run pointer
|
||||
echo "$run_dir" > "$OUTPUT_DIR/last_run_dir.txt"
|
||||
log_info "Saved last run pointer: $OUTPUT_DIR/last_run_dir.txt"
|
||||
|
||||
log_info "Backup complete."
|
||||
log_info "Next: ./scripts/20_encrypt_plan.sh then 21_encrypt_apply.sh"
|
||||
}
|
||||
|
||||
[[ "${BASH_SOURCE[0]}" == "$0" ]] && main "$@"
|
||||
Reference in New Issue
Block a user