From fa0d61ab1125cb8ceebfff226b0c86a2e20c88c9 Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Mon, 24 Aug 2026 10:08:42 -0400 Subject: [PATCH 1/7] Implement TLC Feedback and Razor Feedback Implements a message that does not print to when being invoked by AMTM Implement a hardened mirror idea of Razor with a faster refresh --- .github/workflows/update-merlin-sha256.yml | 116 +++++++++++++++++++ MerlinAU.sh | 124 +++++++++++++++------ 2 files changed, 209 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/update-merlin-sha256.yml diff --git a/.github/workflows/update-merlin-sha256.yml b/.github/workflows/update-merlin-sha256.yml new file mode 100644 index 00000000..21af39e5 --- /dev/null +++ b/.github/workflows/update-merlin-sha256.yml @@ -0,0 +1,116 @@ +name: Update Merlin SHA256 Checksums + +on: + schedule: + # Every 15 minutes, offset from the top of the hour to reduce scheduler congestion. + - cron: '7,22,37,52 * * * *' + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: update-merlin-sha256 + cancel-in-progress: false + +jobs: + scrape-and-commit: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Fetch, parse, and validate SHA256 signatures + shell: bash + run: | + set -euo pipefail + + readonly SOURCE_URL='https://www.asuswrt-merlin.net/download' + readonly TARGET_FILE='merlin-sha256.txt' + readonly MIN_EXPECTED_ENTRIES=5 + + page_file="$(mktemp)" + candidate_file="$(mktemp)" + trap 'rm -f "$page_file" "$candidate_file"' EXIT + + echo "Fetching SHA256 signatures from ${SOURCE_URL}..." + curl --fail --location --silent --show-error \ + --retry 4 --retry-delay 5 --retry-connrefused \ + --connect-timeout 15 --max-time 60 \ + --user-agent 'MerlinAutoUpdate checksum mirror (+https://github.com/ExtremeFiretop/MerlinAutoUpdate-Router)' \ + --output "$page_file" \ + "$SOURCE_URL" + + # Keep the same source section MerlinAU consumes today, but write to a + # temporary candidate so a scrape/parser failure cannot destroy the + # last-known-good mirror in the repository. + sed -n '/<.*>SHA256 signatures:<\/.*>/,/<\/pre>/p' "$page_file" | \ + sed -n '/]*>/,/<\/pre>/p' | \ + sed -e 's/<[^>]*>//g; s/^[[:space:]]*//; s/[[:space:]]*$//' | \ + tr -d '\r' | \ + sed '/^[[:space:]]*$/d' > "$candidate_file" + + echo "Validating candidate checksum list..." + awk -v min_entries="$MIN_EXPECTED_ENTRIES" ' + BEGIN { + valid = 1 + count = 0 + } + { + count++ + + if (NF != 2) { + printf "Invalid field count on line %d: %s\n", NR, $0 > "/dev/stderr" + valid = 0 + next + } + + if (length($1) != 64 || $1 ~ /[^0-9A-Fa-f]/) { + printf "Invalid SHA256 on line %d: %s\n", NR, $1 > "/dev/stderr" + valid = 0 + } + + if (seen[$2]++) { + printf "Duplicate firmware filename on line %d: %s\n", NR, $2 > "/dev/stderr" + valid = 0 + } + } + END { + if (count < min_entries) { + printf "Only %d checksum entries were parsed; expected at least %d.\n", count, min_entries > "/dev/stderr" + valid = 0 + } + + if (!valid) + exit 1 + } + ' "$candidate_file" + + echo "Validated $(wc -l < "$candidate_file") checksum entries." + echo "Candidate preview:" + head -n 5 "$candidate_file" + + # Replace the working-tree copy only after the candidate has passed + # every validation check. A failed run therefore leaves the repository + # and its last-known-good checksum mirror unchanged. + mv -f "$candidate_file" "$TARGET_FILE" + + - name: Commit and push changes + shell: bash + run: | + set -euo pipefail + + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + + git add merlin-sha256.txt + + if git diff --cached --quiet; then + echo 'No checksum changes detected. Nothing to commit.' + exit 0 + fi + + git commit -m 'Automated update: refresh Merlin SHA256 checksums' + git push diff --git a/MerlinAU.sh b/MerlinAU.sh index 6237db63..6f281009 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -9,11 +9,11 @@ set -u ## Set version for each Production Release ## -readonly SCRIPT_VERSION=1.6.6 -readonly SCRIPT_VERSTAG="26081603" +readonly SCRIPT_VERSION=1.6.7 +readonly SCRIPT_VERSTAG="26082409" readonly SCRIPT_NAME="MerlinAU" ## Set to "master" for Production Releases ## -SCRIPT_BRANCH="master" +SCRIPT_BRANCH="dev" ##----------------------------------------## ## Modified by Martinski W. [2024-Jul-03] ## @@ -26,6 +26,9 @@ SCRIPT_URL_REPO="${SCRIPT_URL_BASE}/$SCRIPT_BRANCH" readonly FW_SFURL_BASE="https://sourceforge.net/projects/asuswrt-merlin/files" readonly FW_SFURL_RELEASE_SUFFIX="Release" readonly FW_GITURL_RELEASE="https://api.github.com/repos/gnuton/asuswrt-merlin.ng/releases/latest" +readonly FW_SHA256_URL="https://www.asuswrt-merlin.net/download" +# The scheduled checksum mirror is maintained on the repository's default branch. # +readonly FW_SHA256_MIRROR_URL="${SCRIPT_URL_BASE}/main/merlin-sha256.txt" ##----------------------------------------## ## Modified by Martinski W. [2024-May-31] ## @@ -5971,45 +5974,104 @@ _CopyGnutonFiles_() return 0 } -##----------------------------------------## -## Modified by Martinski W. [2025-Feb-17] ## -##----------------------------------------## +##------------------------------------------## +## Modified by ExtremeFiretop [2026-Aug-24] ## +##------------------------------------------## +##------------------------------------------## +## Modified by ExtremeFiretop [2026-Aug-24] ## +##------------------------------------------## +_GetFirmwareSHA256FromList_() +{ + local checksumList="$1" + local firmwareName="$2" + + # Return a checksum only when there is exactly one exact filename match + # and its digest is a syntactically valid SHA256 value. Using awk avoids + # treating firmware filenames as regular expressions. + printf '%s\n' "$checksumList" | awk -v firmwareName="$firmwareName" ' + $2 == firmwareName && length($1) == 64 && $1 !~ /[^0-9A-Fa-f]/ { + matchCount++ + checksum = tolower($1) + } + END { + if (matchCount == 1) + print checksum + }' +} + _CheckOnlineFirmwareSHA256_() { - # Fetch the latest SHA256 checksums from ASUSWRT-Merlin website # - checksums="$(curl -Ls --retry 4 --retry-delay 5 --retry-connrefused \ - https://www.asuswrt-merlin.net/download | - sed -n '/<.*>SHA256 signatures:<\/.*>/,/<\/pre>/p' | - sed -n '/].*>/,/<\/pre>/p' | - sed -e 's/<[^>].*>//g; s/^[[:space:]]*//; s/[[:space:]]*$//')" + local checksums="" + local dl_sig="" + local fw_name="" + local fw_sig="" + local checksumSource="" - if [ -z "$checksums" ] + if [ ! -f "$firmware_file" ] then - Say "${REDct}**ERROR**${NOct}: Could not download the firmware SHA256 signatures from the website." + Say "${REDct}**ERROR**${NOct}: Firmware image file NOT found!" _DoCleanUp_ 1 return 1 fi - if [ -f "$firmware_file" ] + fw_name="$(basename "$firmware_file")" + fw_sig="$(openssl sha256 "$firmware_file" | awk -F ' ' '{print tolower($2)}')" + + # PRIMARY: Fetch the checksum directly from the ASUSWRT-Merlin website. + checksums="$(curl -Lfs --retry 4 --retry-delay 5 --retry-connrefused \ + "$FW_SHA256_URL" 2>/dev/null | + sed -n '/<.*>SHA256 signatures:<\/.*>/,/<\/pre>/p' | + sed -n '/]*>/,/<\/pre>/p' | + sed -e 's/<[^>]*>//g; s/^[[:space:]]*//; s/[[:space:]]*$//')" + + if [ -n "$checksums" ] then - fw_sig="$(openssl sha256 "$firmware_file" | awk -F ' ' '{print $2}')" - # Extract the corresponding signature for the firmware file from the fetched checksums # - dl_sig="$(echo "$checksums" | grep "$(basename "$firmware_file")" | awk -F ' ' '{print $1}')" - if [ "$fw_sig" != "$dl_sig" ] + dl_sig="$(_GetFirmwareSHA256FromList_ "$checksums" "$fw_name")" + fi + + if [ -n "$dl_sig" ] + then + checksumSource="ASUSWRT-Merlin website" + else + Say "${YLWct}**WARNING**${NOct}: Could not obtain a unique valid SHA256 signature for ${fw_name} from the ASUSWRT-Merlin website." + Say "Trying the independent MerlinAU GitHub checksum mirror..." + + # SECONDARY: Use the repository mirror only when the official source + # did not yield a usable checksum. Never use the checksum bundled in + # the firmware archive for an online update. + checksums="$(curl -Lfs --retry 4 --retry-delay 5 --retry-connrefused \ + "$FW_SHA256_MIRROR_URL" 2>/dev/null)" + + if [ -n "$checksums" ] + then + dl_sig="$(_GetFirmwareSHA256FromList_ "$checksums" "$fw_name")" + fi + + if [ -z "$dl_sig" ] then - Say "${REDct}**ERROR**${NOct}: SHA256 signature from extracted firmware file does not match the SHA256 signature from the website." + Say "${REDct}**ERROR**${NOct}: No unique valid SHA256 signature for ${fw_name} was available from either independent online source." + Say "Online firmware update was aborted; bundled archive checksum fallback is intentionally disabled." _DoCleanUp_ 1 _SendEMailNotification_ FAILED_FW_CHECKSUM_STATUS return 1 - else - Say "SHA256 signature check for firmware image file passed successfully." - return 0 fi - else - Say "${REDct}**ERROR**${NOct}: Firmware image file NOT found!" + + checksumSource="MerlinAU GitHub checksum mirror" + Say "${YLWct}**WARNING**${NOct}: Using the MerlinAU GitHub checksum mirror for verification." + fi + + # If the primary source supplied a checksum but it mismatches, this fails + # immediately. The mirror is never used to bypass a checksum mismatch. + if [ "$fw_sig" != "$dl_sig" ] + then + Say "${REDct}**ERROR**${NOct}: SHA256 signature from extracted firmware file does not match the SHA256 signature from the ${checksumSource}." _DoCleanUp_ 1 + _SendEMailNotification_ FAILED_FW_CHECKSUM_STATUS return 1 fi + + Say "SHA256 signature check for firmware image file passed successfully using the ${checksumSource}." + return 0 } ##----------------------------------------## @@ -10371,9 +10433,9 @@ _DelFWAutoUpdateHook_() fi } -##----------------------------------------## -## Modified by Martinski W. [2024-May-17] ## -##----------------------------------------## +##------------------------------------------## +## Modified by ExtremeFiretop [2026-Aug-24] ## +##------------------------------------------## _AddFWAutoUpdateHook_() { local hookScriptFile jobHookAdded=false @@ -10398,12 +10460,12 @@ _AddFWAutoUpdateHook_() if "$jobHookAdded" then Say "Cron job hook was added successfully to '$hookScriptFile' script." - else Say "Cron job hook already exists in '$hookScriptFile' script." + else DoPrintf "Cron job hook already exists in '$hookScriptFile' script.\n" fi } ##------------------------------------------## -## Modified by ExtremeFiretop [2024-Nov-18] ## +## Modified by ExtremeFiretop [2026-Aug-24] ## ##------------------------------------------## _AddScriptAutoUpdateHook_() { @@ -10429,7 +10491,7 @@ _AddScriptAutoUpdateHook_() if "$jobHookAdded" then Say "Cron job hook was added successfully to '$hookScriptFile' script." - else Say "Cron job hook already exists in '$hookScriptFile' script." + else DoPrintf "Cron job hook already exists in '$hookScriptFile' script.\n" fi } From 9b5578cac0c29bfb37a56923ffe0cbafd05342bf Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Mon, 24 Aug 2026 10:18:46 -0400 Subject: [PATCH 2/7] Debug Workflow Debug Workflow --- .github/workflows/update-merlin-sha256.yml | 32 ++++++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/.github/workflows/update-merlin-sha256.yml b/.github/workflows/update-merlin-sha256.yml index 21af39e5..838531a2 100644 --- a/.github/workflows/update-merlin-sha256.yml +++ b/.github/workflows/update-merlin-sha256.yml @@ -36,12 +36,32 @@ jobs: trap 'rm -f "$page_file" "$candidate_file"' EXIT echo "Fetching SHA256 signatures from ${SOURCE_URL}..." - curl --fail --location --silent --show-error \ - --retry 4 --retry-delay 5 --retry-connrefused \ - --connect-timeout 15 --max-time 60 \ - --user-agent 'MerlinAutoUpdate checksum mirror (+https://github.com/ExtremeFiretop/MerlinAutoUpdate-Router)' \ - --output "$page_file" \ - "$SOURCE_URL" + + http_code="$( + curl --location --silent --show-error \ + --retry 4 --retry-delay 5 --retry-connrefused \ + --connect-timeout 15 --max-time 60 \ + --user-agent 'MerlinAutoUpdate checksum mirror (+https://github.com/ExtremeFiretop/MerlinAutoUpdate-Router)' \ + --output "$page_file" \ + --write-out '%{http_code}' \ + "$SOURCE_URL" + )" + + echo "HTTP response code: ${http_code}" + echo "Downloaded page size: $(wc -c < "$page_file") bytes" + + echo "Relevant response markers:" + grep -iE 'SHA256|signature|forbidden|blocked|access denied|cloudflare|captcha|wix|error' \ + "$page_file" | head -n 30 || true + + echo "First 5000 bytes of returned document:" + head -c 5000 "$page_file" + echo + + if [[ ! "$http_code" =~ ^2[0-9][0-9]$ ]]; then + echo "ERROR: Source returned HTTP ${http_code}." >&2 + exit 1 + fi # Keep the same source section MerlinAU consumes today, but write to a # temporary candidate so a scrape/parser failure cannot destroy the From 4a1010f9b0b8b6ac3592b6fea8b4cde4648220c8 Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Mon, 24 Aug 2026 10:27:28 -0400 Subject: [PATCH 3/7] Revert "Debug Workflow" This reverts commit 9b5578cac0c29bfb37a56923ffe0cbafd05342bf. --- .github/workflows/update-merlin-sha256.yml | 32 ++++------------------ 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/.github/workflows/update-merlin-sha256.yml b/.github/workflows/update-merlin-sha256.yml index 838531a2..21af39e5 100644 --- a/.github/workflows/update-merlin-sha256.yml +++ b/.github/workflows/update-merlin-sha256.yml @@ -36,32 +36,12 @@ jobs: trap 'rm -f "$page_file" "$candidate_file"' EXIT echo "Fetching SHA256 signatures from ${SOURCE_URL}..." - - http_code="$( - curl --location --silent --show-error \ - --retry 4 --retry-delay 5 --retry-connrefused \ - --connect-timeout 15 --max-time 60 \ - --user-agent 'MerlinAutoUpdate checksum mirror (+https://github.com/ExtremeFiretop/MerlinAutoUpdate-Router)' \ - --output "$page_file" \ - --write-out '%{http_code}' \ - "$SOURCE_URL" - )" - - echo "HTTP response code: ${http_code}" - echo "Downloaded page size: $(wc -c < "$page_file") bytes" - - echo "Relevant response markers:" - grep -iE 'SHA256|signature|forbidden|blocked|access denied|cloudflare|captcha|wix|error' \ - "$page_file" | head -n 30 || true - - echo "First 5000 bytes of returned document:" - head -c 5000 "$page_file" - echo - - if [[ ! "$http_code" =~ ^2[0-9][0-9]$ ]]; then - echo "ERROR: Source returned HTTP ${http_code}." >&2 - exit 1 - fi + curl --fail --location --silent --show-error \ + --retry 4 --retry-delay 5 --retry-connrefused \ + --connect-timeout 15 --max-time 60 \ + --user-agent 'MerlinAutoUpdate checksum mirror (+https://github.com/ExtremeFiretop/MerlinAutoUpdate-Router)' \ + --output "$page_file" \ + "$SOURCE_URL" # Keep the same source section MerlinAU consumes today, but write to a # temporary candidate so a scrape/parser failure cannot destroy the From 7b7be96ada99d2c29fa87fd956fa680aade180d4 Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Mon, 24 Aug 2026 10:31:01 -0400 Subject: [PATCH 4/7] Fix Workflow Fix Workflow --- .github/workflows/update-merlin-sha256.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-merlin-sha256.yml b/.github/workflows/update-merlin-sha256.yml index 21af39e5..d1261e53 100644 --- a/.github/workflows/update-merlin-sha256.yml +++ b/.github/workflows/update-merlin-sha256.yml @@ -48,7 +48,10 @@ jobs: # last-known-good mirror in the repository. sed -n '/<.*>SHA256 signatures:<\/.*>/,/<\/pre>/p' "$page_file" | \ sed -n '/]*>/,/<\/pre>/p' | \ - sed -e 's/<[^>]*>//g; s/^[[:space:]]*//; s/[[:space:]]*$//' | \ + sed -e 's/^.*]*>//' \ + -e 's/<[^>]*>//g' \ + -e 's/^[[:space:]]*//' \ + -e 's/[[:space:]]*$//' | \ tr -d '\r' | \ sed '/^[[:space:]]*$/d' > "$candidate_file" From 2ec4ae82f0a85aa1eca844914ff4003efbae9a3a Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Mon, 24 Aug 2026 10:35:14 -0400 Subject: [PATCH 5/7] Authorize the use of secret key Authorize the use of secret key --- .github/workflows/update-merlin-sha256.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/update-merlin-sha256.yml b/.github/workflows/update-merlin-sha256.yml index d1261e53..7bf939be 100644 --- a/.github/workflows/update-merlin-sha256.yml +++ b/.github/workflows/update-merlin-sha256.yml @@ -21,6 +21,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v7 + with: + ssh-key: ${{ secrets.DEPLOY_KEY }} - name: Fetch, parse, and validate SHA256 signatures shell: bash From c6c25848e11c0145b191c5ad4e05a28931c3623c Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Mon, 24 Aug 2026 10:38:44 -0400 Subject: [PATCH 6/7] Match Main Match Main --- merlin-sha256.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 merlin-sha256.txt diff --git a/merlin-sha256.txt b/merlin-sha256.txt new file mode 100644 index 00000000..2eed75ce --- /dev/null +++ b/merlin-sha256.txt @@ -0,0 +1,21 @@ +a04d65e566b70a2533f1dcde9e0e1e66133438268f66165caeb973a2aaa8e065 GT-AX11000_3004_388.12_2_rog_ubi.w +24f8679af3726386321aea8ae49af2e7a560f46a8f1fca5a93e31a31d72e43d2 GT-AX11000_3004_388.12_2_ubi.w +b78b365ec8060351c5143782ad9a438f67ab39f37b454fbe06113e99a62c6c25 GT-AXE11000_3004_388.12_2_pureubi.w +c4ef1675ecd772f381c6e5a696d643db89e28f64451d140f53bb8fea62d0089d GT-AXE11000_3004_388.12_2_rog_pureubi.w +352f4a89e86f02deb89d5569db0e354c89956f28225458f8e14f4fe6bacc2123 RT-AX58U_3004_388.12_2_puresqubi.w +936e04cccc6da05b94c19c3cb9c6d8b9fbb0a6542adc7b953aea3c0c7c068903 RT-AX68U_3004_388.12_2_pureubi.w +864cb94504bd0f036f730089c779aafbf5827721af10d5533924f94d3c20982a RT-AX86U_3004_388.12_2_pureubi.w +a902d77915c609cf5ee6ac6069c52e3ab460cd4b915f0febcb269175cc842a6d RT-AX88U_3004_388.12_2_ubi.w +5dc2284ac99bcb280b8f57c98d817ae17c1ae5e60102b8cb581680894739f467 GT-AX11000_PRO_3006_102.8_4_nand_squashfs.pkgtb +9497ab9a5956da9b6c9b86a1c0172e0eef9d8e02c9531598332f0a911a92e925 GT-AX6000_3006_102.8_4_nand_squashfs.pkgtb +ea266310a61dc9018fed850e1dba38b37335740fd5e0053683a1133d69837f4a GT-AXE16000_3006_102.8_4_nand_squashfs.pkgtb +f9e896c3b46a2913e60ea2ac638b4ca624d4d239b3a5416864da95f8b4ed1873 GT-BE19000AI_3006_102.8_4_emmc_squashfs.pkgtb +11932c048a9ecc2be8de93a19af985d685bb21999091d70a1b946fbfa57b7960 GT-BE98_PRO_3006_102.8_4_nand_squashfs.pkgtb +78f296e30bde73b842f3e211b3205bdab7e9b947f1b0b40d43a248cc86badaa7 RT-AX86U_PRO_3006_102.8_4_nand_squashfs.pkgtb +8eca0813db27ef8518535cd00060475c58c7928d8878e87d34df4476dde05550 RT-AX88U_PRO_3006_102.8_4_nand_squashfs.pkgtb +566bb3a9a6331293d7a28b2dbb0676dfa846a5f11bafa3a99c848b894c087606 RT-BE58_GO_3006_102.8_4_nand_squashfs.pkgtb +7cf298f161aca6de3ad4ff3840f08eae3c11a39c067da825e444439aac67dabc RT-BE86U_3006_102.8_4_nand_squashfs.pkgtb +28954e19157261fe97adcb266591de839c202d188d485484a87089149c61dea3 RT-BE88U_3006_102.8_4_nand_squashfs.pkgtb +1fc818b504f51a378fcbd1b8acde0d1e7950d28527c364cc8c4937c9a4072fbe RT-BE92U_3006_102.8_4_nand_squashfs.pkgtb +f4483a27071d4bc074b2e42760e736a8d079a5f0fb9cab1684553de11ce5f453 RT-BE96U_3006_102.8_4_nand_squashfs.pkgtb +12b00d614e5073e78c8e87ae84c688c2d93e7afcb4d0f36d479731b1cbb9f54e XT12_3006_102.8_4_nand_squashfs.pkgtb From 1f3e371f73ae1519a1d2666b8b931dfb4e58cdb1 Mon Sep 17 00:00:00 2001 From: ExtremeFiretop Date: Mon, 24 Aug 2026 10:53:21 -0400 Subject: [PATCH 7/7] Improve wording Improve wording --- MerlinAU.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MerlinAU.sh b/MerlinAU.sh index 6f281009..6bc3fbc1 100644 --- a/MerlinAU.sh +++ b/MerlinAU.sh @@ -6033,8 +6033,7 @@ _CheckOnlineFirmwareSHA256_() then checksumSource="ASUSWRT-Merlin website" else - Say "${YLWct}**WARNING**${NOct}: Could not obtain a unique valid SHA256 signature for ${fw_name} from the ASUSWRT-Merlin website." - Say "Trying the independent MerlinAU GitHub checksum mirror..." + Say "${YLWct}**WARNING**${NOct}: Independently published checksum could not be retrieved from the ASUSWRT-Merlin website." # SECONDARY: Use the repository mirror only when the official source # did not yield a usable checksum. Never use the checksum bundled in @@ -6057,7 +6056,7 @@ _CheckOnlineFirmwareSHA256_() fi checksumSource="MerlinAU GitHub checksum mirror" - Say "${YLWct}**WARNING**${NOct}: Using the MerlinAU GitHub checksum mirror for verification." + Say "${YLWct}**WARNING**${NOct}: Using the MerlinAU GitHub checksum mirror for verification. (15 MINUTE DELAY!)" fi # If the primary source supplied a checksum but it mismatches, this fails