#!/usr/bin/env bash set -euo pipefail usage() { cat <<'USAGE' Usage: hermes-emergency-bundle-create.sh [output-dir] Creates a GPG-encrypted emergency bundle containing sensitive recovery files that are intentionally excluded from the normal GitHub Hermes backups. Default output-dir: /root/hermes-emergency-bundles Passphrase: Interactive GPG prompt by default. Or set BUNDLE_PASSPHRASE_FILE=/root/path/to/passphrase-file for unattended use. Safety: - Does not print secret values. - Uses an allow-list of sensitive recovery files. - Does not include logs, caches, locks, PIDs, or sandboxes. USAGE } if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then usage exit 0 fi OUT_DIR="${1:-/root/hermes-emergency-bundles}" STAMP="$(date -u +%Y%m%dT%H%M%SZ)" HOST="$(hostname -s 2>/dev/null || hostname)" WORK_DIR="$(mktemp -d)" PAYLOAD_DIR="$WORK_DIR/payload" ARCHIVE="$WORK_DIR/hermes-emergency-bundle-${HOST}-${STAMP}.tar.zst" OUT_FILE="$OUT_DIR/hermes-emergency-bundle-${HOST}-${STAMP}.tar.zst.gpg" cleanup() { rm -rf "$WORK_DIR" } trap cleanup EXIT install -d -m 700 "$OUT_DIR" install -d -m 700 "$PAYLOAD_DIR" copy_if_exists() { src="$1" dest="$PAYLOAD_DIR/${src#/}" if [ -e "$src" ]; then install -d -m 700 "$(dirname "$dest")" cp -a "$src" "$dest" printf '%s\n' "${src#/}" >> "$PAYLOAD_DIR/MANIFEST.paths" fi } # Root Hermes sensitive state. copy_if_exists /root/.hermes/.env copy_if_exists /root/.hermes/auth.json copy_if_exists /root/.hermes/state.db copy_if_exists /root/.hermes/state.db-shm copy_if_exists /root/.hermes/state.db-wal # Uma Hermes sensitive state. copy_if_exists /home/uma/.hermes/.env copy_if_exists /home/uma/.hermes/auth.json copy_if_exists /home/uma/.hermes/state.db copy_if_exists /home/uma/.hermes/state.db-shm copy_if_exists /home/uma/.hermes/state.db-wal # Git and local registry credentials used for recovery operations. copy_if_exists /root/.git-credentials copy_if_exists /root/.gitea_admin_password copy_if_exists /root/.gitea_npm_token copy_if_exists /root/.gitea_npm_token_home # Tailscale machine state is sensitive. Restoring it is optional; a fresh # `tailscale up` login is often cleaner, but this preserves a break-glass copy. copy_if_exists /var/lib/tailscale/tailscaled.state if [ ! -s "$PAYLOAD_DIR/MANIFEST.paths" ]; then echo "No emergency files found to bundle." >&2 exit 1 fi cat > "$PAYLOAD_DIR/README.txt" <