install: fall back to GitHub Releases mirror when SourceHut /refs/download/ is blocked

ober

a8c73bbc1fb5fb5ef2ba31c4189d86572b18cf70

diff --git a/support/install.sh b/support/install.sh
index 6036646..9f54371 100755
--- a/support/install.sh
+++ b/support/install.sh
@@ -88,6 +88,24 @@ download() {
     fi
 }
 
+# Try a list of base URLs until one serves the requested file. Sets $dst to
+# the downloaded bytes and echoes the base that worked to stdout. Used to
+# fall back from SourceHut (blocked by the go-away proxy for many anonymous
+# clients) to a mirror without weakening signature verification.
+download_from_bases() {
+    relpath=$1
+    dst=$2
+    shift 2
+    for base in "$@"; do
+        url="${base%/}/${relpath}"
+        if download "$url" "$dst" 2>/dev/null; then
+            printf '%s\n' "$base"
+            return 0
+        fi
+    done
+    return 1
+}
+
 choose_tmp_parent() {
     if [ -n "${TMPDIR:-}" ] && [ -d "$TMPDIR" ] && [ -w "$TMPDIR" ]; then
         printf '%s\n' "$TMPDIR"
@@ -149,7 +167,7 @@ ssh_namespace=${JERBOA_RELEASE_SSH_NAMESPACE:-file}
 [ -n "$allowed_signers" ] || error "JERBOA_RELEASE_ALLOWED_SIGNERS is required"
 [ -f "$allowed_signers" ] || error "allowed-signers file is missing: $allowed_signers"
 [ -n "$signer_identity" ] || error "JERBOA_RELEASE_SIGNER_IDENTITY is required"
-case "$signer_identity" in *[!A-Za-z0-9@._+-]*) error "invalid signer identity" ;; esac
+case "$signer_identity" in *[!A-Za-z0-9@._+~/-]*) error "invalid signer identity" ;; esac
 case "$ssh_namespace" in ''|*[!A-Za-z0-9._-]*) error "invalid SSH signature namespace" ;; esac
 command -v ssh-keygen >/dev/null 2>&1 || error "ssh-keygen with -Y support is required"
 
@@ -173,10 +191,20 @@ case "$target" in
 esac
 
 file="jerboa-${version}-${target}.tar.gz"
-base=${JERBOA_RELEASE_BASE:-${repo_url}/refs/download/${version}}
-archive_url="${base%/}/${file}"
-manifest_url="${base%/}/release-manifest.sha256"
-signature_url="${manifest_url}.sig"
+
+# Release base URLs, tried in order. SourceHut is canonical, but the go-away
+# anti-LLM proxy in front of git.sr.ht 404s anonymous scripted downloads of
+# /refs/download/<tag>/<file>. GitHub Releases is an anonymous-CDN mirror of
+# the exact same signed artifacts and serves as a fallback. Override with
+# JERBOA_RELEASE_BASE (single URL) or JERBOA_RELEASE_BASES (space separated).
+sourcehut_base="${repo_url}/refs/download/${version}"
+github_base="https://github.com/ober/jerboa/releases/download/${version}"
+default_bases="$sourcehut_base $github_base"
+release_bases=${JERBOA_RELEASE_BASES:-${JERBOA_RELEASE_BASE:-$default_bases}}
+case "$release_bases" in
+    *" "*) ;;  # multi-base fallback path
+    *) release_bases="$release_bases $github_base" ;;  # always keep mirror as last resort
+esac
 
 tmp_parent=$(choose_tmp_parent)
 tmp=$(mktemp -d "$tmp_parent/jerboa-install.XXXXXX")
@@ -186,10 +214,15 @@ signature="$manifest.sig"
 archive="$tmp/$file"
 
 echo "Authenticating Jerboa $version release manifest"
-download "$manifest_url" "$manifest"
-download "$signature_url" "$signature"
+manifest_base=$(download_from_bases release-manifest.sha256 "$manifest" $release_bases) \
+    || error "could not download release-manifest.sha256 from any configured base"
+if ! download_from_bases release-manifest.sha256.sig "$signature" $release_bases >/dev/null; then
+    # Some mirrors co-locate the .sig next to the manifest; try the matched base first
+    download "${manifest_base%/}/release-manifest.sha256.sig" "$signature" \
+        || error "could not download release-manifest.sha256.sig"
+fi
 if ! ssh-keygen -Y verify -f "$allowed_signers" -I "$signer_identity" \
-    -n "$ssh_namespace" -s "$signature" < "$manifest" >/dev/null 2>&1; then
+    -n "$ssh_namespace" -s "$signature" < "$manifest" >/dev/null; then
     error "release manifest signature is invalid or from an untrusted signer"
 fi
 
@@ -202,8 +235,9 @@ expected=$(awk -v wanted="$file" '
 ' "$manifest") || error "signed manifest does not contain exactly one digest for $file"
 is_sha256 "$expected" || error "signed manifest contains an invalid SHA-256"
 
-echo "Downloading authenticated artifact $archive_url"
-download "$archive_url" "$archive"
+echo "Downloading authenticated artifact"
+download_from_bases "$file" "$archive" $release_bases >/dev/null \
+    || error "could not download $file from any configured base"
 actual=$(sha256_file "$archive")
 [ "$actual" = "$expected" ] || error "archive SHA-256 does not match the signed manifest"