updates

ober

425c0ae994b02773a0db939279645381cf87abf1

diff --git a/data/anti-patterns.sexp b/data/anti-patterns.sexp
index 0e19139..7cedcfa 100644
--- a/data/anti-patterns.sexp
+++ b/data/anti-patterns.sexp
@@ -5908,4 +5908,41 @@
      "jerboa_check_balance"
      "jerboa_compile_check"
      "jerboa_security_scan"))
- )
+ (("advice"
+    .
+    "Download official release artifacts exclusively from versioned SourceHut tag URLs. Reserve alternate base URLs only for explicit local test fixtures or separate fork tooling, never as an automatic fallback.")
+   ("avoid"
+     .
+     "Do not add a mirror or fallback download path to the official Jerboa release installer.")
+   ("id" . "official-release-installer-mirror-fallback")
+   ("kinds" "script" "security")
+   ("pattern"
+     .
+     "download_from_bases|JERBOA_RELEASE_BASE.*mirror")
+   ("severity" . "high")
+   ("tags" "release" "installer" "sourcehut" "artifact"
+     "supply-chain")
+   ("title"
+     .
+     "Official release installer falls back to non-official mirror")
+   ("tools"
+     "jerboa_security_scan"
+     "jerboa_anti_pattern_lookup"))
+ (("advice"
+    .
+    "For mutexes use the (std misc thread) set together via (prefix (only (std misc thread) make-mutex mutex-lock! mutex-unlock!) mt:) and call mt:make-mutex/mt:mutex-lock!/mt:mutex-unlock! (gerbil-mutex, mutually compatible). To block the main thread use (std misc thread) thread-sleep! in a loop. For filtering lists write an explicit recursive loop (or filter) instead of prelude keep.")
+   ("avoid"
+     .
+     "Mixing prelude make-mutex (Chez #<mutex>) with (std misc thread) mutex-lock! (gerbil-mutex) -> runtime error '#<mutex> is not of type gerbil-mutex'. Using prelude 'keep' to filter -> returned (#t #t) instead of kept elements. Using (sleep (make-time 'time-duration 0 1)) -> 'not a time record of type time-duration'.")
+   ("id" . "jerboa-prelude-keep-sleep-mutex-traps")
+   ("kinds" "jerboa" "scheme" "debugging")
+   ("pattern"
+     .
+     "(make-mutex)|(mutex-lock!)|(keep )|(sleep \\(make-time)")
+   ("severity" . "high")
+   ("tags" "jerboa" "prelude" "mutex" "keep" "sleep" "thread"
+     "stdlib")
+   ("title"
+     .
+     "Jerboa prelude traps: keep, sleep, and make-mutex mismatch")
+   ("tools" "jerboa_eval" "jerboa_module_exports")))
diff --git a/docs/release-artifacts.md b/docs/release-artifacts.md
index f36298b..7161d7b 100644
--- a/docs/release-artifacts.md
+++ b/docs/release-artifacts.md
@@ -48,26 +48,33 @@ and Chez NOTICE text alongside the executable.
 
 ## End-User Install
 
-Do not pipe the network installer to a shell. Download the versioned installer
-and detached signature, authenticate it against a Jerboa release key obtained
-through an independent trusted channel, inspect it, and run the local file.
-For example, after substituting the reviewed release identity and locally
-provisioned allowed-signers file:
+Do not pipe the network installer to a shell. A secure install has one
+irreducible bootstrap step: obtain the Jerboa release public key and its signer
+identity through a channel independent of the download (for example, a
+maintainer's verified fingerprint). Save that information as an OpenSSH
+allowed-signers file. The project must publish the exact file contents,
+fingerprint, and identity before a release can claim to offer an end-user
+authenticated install.
+
+Once you have that small local trust file, the Linux AMD64 install is:
 
 ```sh
-version=v0.2.4
+version=vX.Y.Z # exact release tag
 base="https://git.sr.ht/~lisp/jerboa/refs/download/$version"
+trust_file="$HOME/.config/jerboa/release.allowed_signers"
+identity='jerboa-release@<publisher-domain>'
+
+mkdir -p "$(dirname "$trust_file")"
+# Create $trust_file from the independently verified release-key information.
 curl --fail --proto '=https' --proto-redir '=https' \
   -o install.sh "$base/install.sh"
 curl --fail --proto '=https' --proto-redir '=https' \
   -o install.sh.sig "$base/install.sh.sig"
 ssh-keygen -Y verify \
-  -f /path/to/independently-provisioned/jerboa-release.allowed_signers \
-  -I '<reviewed-release-identity>' -n file -s install.sh.sig < install.sh
+  -f "$trust_file" -I "$identity" -n file -s install.sh.sig < install.sh
 chmod 0755 install.sh
-JERBOA_RELEASE_ALLOWED_SIGNERS=/path/to/independently-provisioned/jerboa-release.allowed_signers \
-JERBOA_RELEASE_SIGNER_IDENTITY='<reviewed-release-identity>' \
-  ./install.sh --version "$version"
+./install.sh --version "$version" \
+  --allowed-signers "$trust_file" --signer-identity "$identity"
 ```
 
 The installer requires an exact version; mutable `latest` resolution is not an
@@ -89,7 +96,10 @@ JERBOA_INSTALL_DIR="$HOME/bin" ./install.sh --version "$version"
 The canonical installer source remains `support/install.sh`. Release signing
 stages that reviewed file as a versioned release asset and signs both it and the
 release manifest. No official installation path is enabled until the project
-release identity has been provisioned independently to the consumer.
+release identity has been provisioned independently to the consumer. The
+installer accepts `--allowed-signers` and `--signer-identity` so its required
+trust inputs are visible in the command rather than hidden in environment
+variables.
 
 ## Build Locally
 
@@ -436,7 +446,8 @@ https://git.sr.ht/~lisp/jerboa/refs/download/<tag>/<artifact>
 ```
 
 The allowed-signers file must be committed or provisioned by the consuming
-project, not downloaded beside the artifact. Override `JERBOA_RELEASE_REPO`,
-`JERBOA_RELEASE_ORIGIN`, or `JERBOA_RELEASE_BASE` for mirrors or forks. Override
-`JERBOA_RELEASE_TARGET` only for tests or unusual hosts where `uname` detection
-is not enough.
+project, not downloaded beside the artifact. Override `JERBOA_RELEASE_REPO` or
+`JERBOA_RELEASE_ORIGIN` only for a fork. `JERBOA_RELEASE_BASE` is reserved for
+local test fixtures; official artifacts are downloaded only from SourceHut.
+Override `JERBOA_RELEASE_TARGET` only for tests or unusual hosts where `uname`
+detection is not enough.
diff --git a/foo b/foo
deleted file mode 100644
index e69de29..0000000
--- a/foo
+++ /dev/null
diff --git a/support/install.sh b/support/install.sh
index 567dc7d..41b20c5 100755
--- a/support/install.sh
+++ b/support/install.sh
@@ -11,6 +11,7 @@ umask 077
 usage() {
     cat >&2 <<'EOF'
 Usage: install.sh --version VERSION [--bindir DIR | --prefix DIR]
+                  [--allowed-signers FILE --signer-identity ID]
 
 Installs the base Jerboa toolchain from authenticated release artifacts.
 
@@ -18,6 +19,10 @@ Options:
   --version VERSION   Install an exact tag such as v0.2.4 (required)
   --bindir DIR        Install commands directly into DIR
   --prefix DIR        Install commands into DIR/bin
+  --allowed-signers FILE
+                     Local OpenSSH allowed-signers trust file
+  --signer-identity ID
+                     Expected signer identity in the trust file
   -h, --help          Show this help
 
 Required trust configuration:
@@ -88,24 +93,6 @@ 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"
@@ -146,12 +133,16 @@ install_links() {
 version=${JERBOA_VERSION:-}
 bindir=${JERBOA_INSTALL_DIR:-}
 prefix=${PREFIX:-}
+allowed_signers=${JERBOA_RELEASE_ALLOWED_SIGNERS:-}
+signer_identity=${JERBOA_RELEASE_SIGNER_IDENTITY:-}
 
 while [ "$#" -gt 0 ]; do
     case "$1" in
         --version) need_value "$1" "${2:-}"; version=$2; shift 2 ;;
         --bindir) need_value "$1" "${2:-}"; bindir=$2; shift 2 ;;
         --prefix) need_value "$1" "${2:-}"; prefix=$2; shift 2 ;;
+        --allowed-signers) need_value "$1" "${2:-}"; allowed_signers=$2; shift 2 ;;
+        --signer-identity) need_value "$1" "${2:-}"; signer_identity=$2; shift 2 ;;
         -h|--help) usage; exit 0 ;;
         *) error "unknown option: $1" ;;
     esac
@@ -161,8 +152,6 @@ done
 printf '%s\n' "$version" | grep -Eq '^v[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' || \
     error "version must be an exact tag like v0.2.4"
 
-allowed_signers=${JERBOA_RELEASE_ALLOWED_SIGNERS:-}
-signer_identity=${JERBOA_RELEASE_SIGNER_IDENTITY:-}
 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"
@@ -192,9 +181,8 @@ esac
 
 file="jerboa-${version}-${target}.tar.gz"
 
-# Release base URL. SourceHut git-tag artifacts are the only release download
-# channel. Override it with JERBOA_RELEASE_BASE when using an independently
-# operated mirror.
+# Release base URL. SourceHut git-tag artifacts are the only official release
+# download channel. JERBOA_RELEASE_BASE exists solely for local test fixtures.
 sourcehut_base="${repo_url}/refs/download/${version}"
 release_bases=${JERBOA_RELEASE_BASE:-$sourcehut_base}
 
@@ -206,13 +194,10 @@ signature="$manifest.sig"
 archive="$tmp/$file"
 
 echo "Authenticating Jerboa $version release manifest"
-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
+download "${release_bases%/}/release-manifest.sha256" "$manifest" \
+    || error "could not download release-manifest.sha256"
+download "${release_bases%/}/release-manifest.sha256.sig" "$signature" \
+    || error "could not download release-manifest.sha256.sig"
 if ! ssh-keygen -Y verify -f "$allowed_signers" -I "$signer_identity" \
     -n "$ssh_namespace" -s "$signature" < "$manifest" >/dev/null; then
     error "release manifest signature is invalid or from an untrusted signer"
@@ -228,8 +213,8 @@ expected=$(awk -v wanted="$file" '
 is_sha256 "$expected" || error "signed manifest contains an invalid SHA-256"
 
 echo "Downloading authenticated artifact"
-download_from_bases "$file" "$archive" $release_bases >/dev/null \
-    || error "could not download $file from any configured base"
+download "${release_bases%/}/$file" "$archive" \
+    || error "could not download $file"
 actual=$(sha256_file "$archive")
 [ "$actual" = "$expected" ] || error "archive SHA-256 does not match the signed manifest"
 
diff --git a/tests/test-supply-chain-policy.sh b/tests/test-supply-chain-policy.sh
index b427ba9..2e7e2b8 100755
--- a/tests/test-supply-chain-policy.sh
+++ b/tests/test-supply-chain-policy.sh
@@ -10,7 +10,7 @@ fail() {
 }
 
 must_contain() {
-    grep -Fq "$2" "$1" || fail "$1 is missing: $2"
+    grep -Fq -- "$2" "$1" || fail "$1 is missing: $2"
 }
 
 tmp=$(mktemp -d "${TMPDIR:-/tmp}/jerboa-supply-test.XXXXXX")
@@ -118,6 +118,8 @@ fi
 # The installer requires a consumer-controlled trust root and never trusts a
 # same-origin checksum or extracts arbitrary archive paths.
 must_contain support/install.sh 'JERBOA_RELEASE_ALLOWED_SIGNERS is required'
+must_contain support/install.sh '--allowed-signers FILE'
+must_contain support/install.sh '--signer-identity ID'
 must_contain support/install.sh 'ssh-keygen -Y verify'
 must_contain support/install.sh 'an exact --version is required'
 must_contain support/install.sh 'tar -xOzf'
@@ -169,12 +171,12 @@ ssh-keygen -Y sign -q -f "$tmp/release-key" -n file "$release/release-manifest.s
 printf 'jerboa-release@example.test %s\n' "$(cat "$tmp/release-key.pub")" > "$tmp/allowed-signers"
 
 JERBOA_INSTALL_ALLOW_FILE_URL=1 \
-JERBOA_RELEASE_ALLOWED_SIGNERS="$tmp/allowed-signers" \
-JERBOA_RELEASE_SIGNER_IDENTITY=jerboa-release@example.test \
 JERBOA_RELEASE_BASE="file://$release" \
 JERBOA_RELEASE_TARGET="$target" \
 JERBOA_INSTALL_DIR="$tmp/bin" \
-    support/install.sh --version "$version" > "$tmp/install.out"
+    support/install.sh --version "$version" \
+        --allowed-signers "$tmp/allowed-signers" \
+        --signer-identity jerboa-release@example.test > "$tmp/install.out"
 "$tmp/bin/jerboa" | grep -Fq authenticated-test-binary || fail 'authenticated binary was not installed'
 [ "$(readlink "$tmp/bin/jmcp")" = jerboa ] || fail 'multicall link was not installed'