# Canonical paths follow ownership. Shared callers live under the receiving service. secret_moves() { cat <<'PATHS' bff services/bff/jwt caller services/notifications/callers/bff email services/notifications/email/resend nats infrastructure/nats/auth PATHS } # Archive readable version history privately before removing an obsolete KV path. archive_secret() ( old=$1 metadata=$(vault "kv/metadata/$old" GET yes) [ "$metadata" != null ] || exit 0 directory="$STATE/migrations/organized-v1/$old" mkdir -p "$directory" printf '%s' "$metadata" | write "$directory/metadata.json" printf '%s' "$metadata" | jq -r '.data.versions|to_entries[]|select(.value.destroyed==false and .value.deletion_time=="")|.key' > "$WORK/versions" while IFS= read -r version; do vault "kv/data/$old?version=$version" | write "$directory/version-$version.json" done < "$WORK/versions" ) migrate_secrets() ( secret_moves > "$WORK/moves" while read -r old new; do kv "$old" > "$WORK/old" [ "$(cat "$WORK/old")" != null ] || continue archive_secret "$old" kv "$new" > "$WORK/new" if [ "$(cat "$WORK/new")" = null ]; then # CAS=0 prevents overwriting a destination created concurrently. jq '{data:.,options:{cas:0}}' "$WORK/old" | vault "kv/data/$new" POST >/dev/null fi kv "$new" > "$WORK/new" jq -e -s '.[0]==.[1]' "$WORK/old" "$WORK/new" >/dev/null || fail "Conflicting Vault values at $old and $new; no source removed" done < "$WORK/moves" ) retire_secrets() ( # Call only after templates, policies and deployed images use canonical paths. secret_moves > "$WORK/moves" while read -r old new; do kv "$old" > "$WORK/old" [ "$(cat "$WORK/old")" != null ] || continue kv "$new" > "$WORK/new" jq -e -s '.[0]==.[1]' "$WORK/old" "$WORK/new" >/dev/null || fail "Vault migration verification failed for $old" archive_secret "$old" printf '{}' | vault "kv/metadata/$old" DELETE >/dev/null done < "$WORK/moves" for old in tls frontend-tls notifications-tls; do archive_secret "$old" if [ "$(vault "kv/metadata/$old" GET yes)" != null ]; then printf '{}' | vault "kv/metadata/$old" DELETE >/dev/null; fi done # The obsolete PKI mount is deliberately not disabled: its private signing key # and history are retained until the owner explicitly chooses to destroy them. echo 'PASS: Vault secrets migrated and verified; obsolete KV entries retired' )