#!/usr/bin/env bash set -euo pipefail REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" DEST="${VAULT_PASS_FILE:-$HOME/.config/vault-pass.txt}" ARCHIVE="${1:-$REPO_DIR/secrets/vault-pass.txt.zip}" VAULT_FILE="${VAULT_FILE:-$REPO_DIR/secrets/vault.yml}" # Optional non-interactive controls: # INSTALL_VAULT_PASS_METHOD=create|manual|url|archive # VAULT_PASS_CONTENT= (for method=manual) # VAULT_PASS_URL= (for method=url) # VAULT_PASS_ZIP_PASSWORD= (for method=archive; avoid chat/log) # VAULT_PASS_ZIP_PASSWORD_FILE= (for method=archive; safer than env) usage() { cat </dev/null 2>&1; then echo "Missing dependency: $1" >&2 echo "Please install it first." >&2 exit 3 fi } create_new_password() { require_cmd ansible-vault require_cmd python3 ensure_dest_dir umask 077 python3 - <<'PY' > "$DEST" import secrets print(secrets.token_urlsafe(48)) PY secure_dest echo "Created new vault password file: $DEST" if [ -f "$VAULT_FILE" ]; then if ansible-vault view "$VAULT_FILE" --vault-password-file "$DEST" >/dev/null 2>&1; then echo "Existing vault is already readable with the new password. No re-encryption needed." else cat < "$tmp" <<'YAML' # Initial placeholder vault. Replace with real secrets using ./scripts/vault.sh edit. gitea: {} openclaw_alice: http_nodes: {} ssh_nodes: {} YAML cp "$tmp" "$VAULT_FILE" ansible-vault encrypt "$VAULT_FILE" --vault-password-file "$DEST" rm -f "$tmp" echo "Created encrypted placeholder vault: $VAULT_FILE" fi } manual_create() { ensure_dest_dir if [ -n "${VAULT_PASS_CONTENT:-}" ]; then umask 077 printf '%s\n' "$VAULT_PASS_CONTENT" > "$DEST" else cat <&2 exit 4 fi umask 077 printf '%s\n' "$pass" > "$DEST" fi secure_dest echo "Installed manually provided vault password file: $DEST" } download_from_url() { ensure_dest_dir url="${VAULT_PASS_URL:-}" if [ -z "$url" ]; then printf 'Enter vault-pass.txt URL: ' read -r url fi if [ -z "$url" ]; then echo "URL is required." >&2 exit 4 fi case "$url" in http://*|https://*) ;; *) echo "Only http:// or https:// URLs are supported." >&2; exit 4 ;; esac if command -v curl >/dev/null 2>&1; then umask 077 curl -fsSL "$url" -o "$DEST" elif command -v wget >/dev/null 2>&1; then umask 077 wget -qO "$DEST" "$url" else echo "Missing dependency: curl or wget" >&2 exit 3 fi if [ ! -s "$DEST" ]; then echo "Downloaded file is empty or missing." >&2 exit 4 fi secure_dest echo "Downloaded vault password file to: $DEST" } extract_from_archive() { require_cmd unzip ensure_dest_dir if [ ! -f "$ARCHIVE" ]; then cat >&2 <&2 exit 4 fi zip_pass="$(cat "$VAULT_PASS_ZIP_PASSWORD_FILE")" unzip -P "$zip_pass" -q "$ARCHIVE" -d "$tmpdir" elif [ -n "${VAULT_PASS_ZIP_PASSWORD:-}" ]; then unzip -P "$VAULT_PASS_ZIP_PASSWORD" -q "$ARCHIVE" -d "$tmpdir" else # unzip will prompt for the archive password interactively. unzip -q "$ARCHIVE" -d "$tmpdir" fi src="$tmpdir/vault-pass.txt" if [ ! -f "$src" ]; then echo "Archive extracted, but vault-pass.txt was not found inside." >&2 exit 4 fi install -m 600 "$src" "$DEST" echo "Installed vault password file from archive: $DEST" } verify_vault_readable_if_possible() { if [ -f "$VAULT_FILE" ] && command -v ansible-vault >/dev/null 2>&1; then if ansible-vault view "$VAULT_FILE" --vault-password-file "$DEST" >/dev/null 2>&1; then echo "Verified: vault.yml is readable with $DEST" else echo "Warning: vault.yml is not readable with $DEST" >&2 return 5 fi fi } run_method() { case "$1" in create|1) create_new_password ;; manual|2) manual_create ;; url|3) download_from_url ;; archive|4) extract_from_archive ;; *) echo "Invalid setup method: $1" >&2; exit 4 ;; esac } if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then usage exit 0 fi if verify_existing; then verify_vault_readable_if_possible || true exit 0 fi if [ -n "${INSTALL_VAULT_PASS_METHOD:-}" ]; then run_method "$INSTALL_VAULT_PASS_METHOD" verify_vault_readable_if_possible || true exit 0 fi cat <