From 0408252c83710ba0d76a86eaeb610d603f4c3154 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Sat, 21 Feb 2026 00:06:16 -0800 Subject: [PATCH 01/21] Add macOS ARM64 brew-ready release bundle and signing flow. Package Homebrew-friendly macOS artifacts with libexec/chiavdf dylibs, switch hardware binary rpaths to @loader_path-relative lookup, and move ad-hoc signing to an optional local escape hatch while adding release-time signing/notarization upload paths. --- .github/workflows/vdf-client-hw.yml | 127 +++++++++++++++++++++++++++- assets/macos-release-bundle.md | 39 +++++++++ scripts/get-libft4222.sh | 16 ++-- src/CMakeLists.txt | 4 +- src/Makefile.vdf-client | 4 +- 5 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 assets/macos-release-bundle.md diff --git a/.github/workflows/vdf-client-hw.yml b/.github/workflows/vdf-client-hw.yml index 0613ecd9..7db44599 100644 --- a/.github/workflows/vdf-client-hw.yml +++ b/.github/workflows/vdf-client-hw.yml @@ -58,6 +58,28 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Test for Apple signing secrets + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' + id: check_secrets + shell: bash + run: | + unset HAS_APPLE_SECRET + if [ -n "$APPLE_SECRET" ]; then HAS_APPLE_SECRET='true'; fi + echo "HAS_APPLE_SECRET=${HAS_APPLE_SECRET}" >> "$GITHUB_OUTPUT" + env: + APPLE_SECRET: "${{ secrets.APPLE_DEV_ID_APP }}" + + - name: Delete keychain if it already exists + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' + run: security delete-keychain signing_temp.keychain || true + + - name: Import Apple app signing certificate + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' + uses: Apple-Actions/import-codesign-certs@v6 + with: + p12-file-base64: ${{ secrets.APPLE_DEV_ID_APP }} + p12-password: ${{ secrets.APPLE_DEV_ID_APP_PASS }} + - name: Install macOS deps (build + runtime) if: startsWith(matrix.os, 'macos') run: | @@ -364,6 +386,95 @@ jobs: .\vdf_bench.exe square_asm 2000000 if ($LASTEXITCODE -ne 0) { throw "vdf_bench failed with exit code $LASTEXITCODE" } + - name: Assemble macOS ARM64 brew bundle + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' + env: + RELEASE_TAG: ${{ github.event.release.tag_name }} + HAS_APPLE_SECRET: ${{ steps.check_secrets.outputs.HAS_APPLE_SECRET || '' }} + APPLE_NOTARIZE_USERNAME: "${{ secrets.APPLE_NOTARIZE_USERNAME }}" + APPLE_NOTARIZE_PASSWORD: "${{ secrets.APPLE_NOTARIZE_PASSWORD }}" + APPLE_TEAM_ID: "${{ secrets.APPLE_TEAM_ID }}" + NOTARIZE: ${{ steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' }} + run: | + set -euo pipefail + + BASE_NAME="chiavdf-${RELEASE_TAG}-macos-arm64" + if [ "$HAS_APPLE_SECRET" = "true" ]; then + ASSET_NAME="${BASE_NAME}.zip" + else + ASSET_NAME="${BASE_NAME}-unsigned.zip" + fi + + BUNDLE_ROOT="dist/macos/${BASE_NAME}" + BIN_DIR="${BUNDLE_ROOT}/bin" + LIBEXEC_DIR="${BUNDLE_ROOT}/libexec/chiavdf" + mkdir -p "$BIN_DIR" "$LIBEXEC_DIR" + + cp src/hw_vdf_client "$BIN_DIR/" + cp src/emu_hw_vdf_client "$BIN_DIR/" + cp src/hw_test "$BIN_DIR/" + cp src/emu_hw_test "$BIN_DIR/" + cp src/vdf_client "$BIN_DIR/" + cp src/vdf_bench "$BIN_DIR/" + + cp src/hw/libft4222/libftd2xx.dylib "$LIBEXEC_DIR/" + cp src/hw/libft4222/libft4222.1.4.4.190.dylib "$LIBEXEC_DIR/" + ln -sf "libft4222.1.4.4.190.dylib" "$LIBEXEC_DIR/libft4222.dylib" + + install_name_tool -id "@rpath/libftd2xx.dylib" "$LIBEXEC_DIR/libftd2xx.dylib" + install_name_tool -id "@rpath/libft4222.dylib" "$LIBEXEC_DIR/libft4222.1.4.4.190.dylib" + install_name_tool -change "libftd2xx.dylib" "@rpath/libftd2xx.dylib" "$LIBEXEC_DIR/libft4222.1.4.4.190.dylib" + + for exe in "$BIN_DIR/"*; do + install_name_tool -delete_rpath "@executable_path/hw/libft4222" "$exe" || true + install_name_tool -delete_rpath "@loader_path/../libexec/chiavdf" "$exe" || true + install_name_tool -add_rpath "@loader_path/../libexec/chiavdf" "$exe" + done + + for exe in "$BIN_DIR/"*; do + otool -L "$exe" + otool -l "$exe" | rg "@loader_path/../libexec/chiavdf" + if otool -L "$exe" | rg -q "${GITHUB_WORKSPACE}|/Users/|/private/var/folders"; then + echo "Found non-portable absolute library path in ${exe}" >&2 + exit 1 + fi + done + + SIGNING_STATUS="unsigned (no Apple signing secrets available)" + if [ "$HAS_APPLE_SECRET" = "true" ] && [ "${NOTARIZE}" = "true" ]; then + SIGNING_IDENTITY="$(security find-identity -v -p codesigning | rg 'Developer ID Application' | awk -F\" 'NR==1{print $2}')" + if [ -z "$SIGNING_IDENTITY" ]; then + echo "No Developer ID Application identity found after certificate import." >&2 + exit 1 + fi + + codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$LIBEXEC_DIR/libftd2xx.dylib" + codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$LIBEXEC_DIR/libft4222.1.4.4.190.dylib" + codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$LIBEXEC_DIR/libft4222.dylib" + for exe in "$BIN_DIR/"*; do + codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$exe" + done + + (cd dist/macos && ditto -c -k --keepParent "${BASE_NAME}" "${ASSET_NAME}") + xcrun notarytool submit "dist/macos/${ASSET_NAME}" --apple-id "$APPLE_NOTARIZE_USERNAME" --password "$APPLE_NOTARIZE_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait + SIGNING_STATUS="signed + notarized" + else + (cd dist/macos && ditto -c -k --keepParent "${BASE_NAME}" "${ASSET_NAME}") + fi + + printf "macOS ARM64 release bundle status: %s\n" "$SIGNING_STATUS" > "dist/macos/${BASE_NAME}.signing-status.txt" + shasum -a 256 "dist/macos/${ASSET_NAME}" | awk '{print $1}' > "dist/macos/${ASSET_NAME}.sha256" + + - name: Upload macOS ARM64 bundle artifact + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' + uses: actions/upload-artifact@v6 + with: + name: macos-arm64-brew-bundle + path: | + dist/macos/*.zip + dist/macos/*.sha256 + dist/macos/*.signing-status.txt + - name: Upload binaries artifact (Ubuntu/macOS) if: matrix.os != 'windows-latest' && matrix.config == 'optimized=1' uses: actions/upload-artifact@v6 @@ -433,16 +544,30 @@ jobs: name: installer path: dist/*.deb - - name: Upload release artifacts + - name: Upload Ubuntu release artifacts if: matrix.os == 'ubuntu-latest' && matrix.config == 'optimized=1' && github.event_name == 'release' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_TAG: ${{ github.event.release.tag_name }} run: | gh release upload \ + --clobber \ $RELEASE_TAG \ dist/*.deb + - name: Upload macOS ARM64 release artifacts + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: | + gh release upload \ + --clobber \ + $RELEASE_TAG \ + dist/macos/*.zip \ + dist/macos/*.sha256 \ + dist/macos/*.signing-status.txt + - uses: Chia-Network/actions/github/jwt@main if: matrix.os == 'ubuntu-latest' && matrix.config == 'optimized=1' && github.event_name == 'release' diff --git a/assets/macos-release-bundle.md b/assets/macos-release-bundle.md new file mode 100644 index 00000000..a2198170 --- /dev/null +++ b/assets/macos-release-bundle.md @@ -0,0 +1,39 @@ +# macOS ARM64 release bundle for Homebrew + +This repository publishes a macOS ARM64 release archive intended for Homebrew/cask consumption. + +## Archive layout + +- `bin/` + - `hw_vdf_client` + - `emu_hw_vdf_client` + - `hw_test` + - `emu_hw_test` + - `vdf_client` + - `vdf_bench` +- `libexec/chiavdf/` + - `libft4222.dylib` + - `libft4222.1.4.4.190.dylib` + - `libftd2xx.dylib` + +## Dynamic library path policy + +- Hardware binaries are linked with `@loader_path/../libexec/chiavdf` rpath on macOS. +- FTDI dylibs use `@rpath` install names. +- CI verifies with `otool` that release binaries do not reference local absolute build paths. + +## Signing and notarization behavior + +- Release run with Apple secrets available: + - Sign FTDI dylibs and binaries with Developer ID. + - Notarize the final release zip with `notarytool`. +- Release run without Apple secrets: + - Publish an unsigned fallback zip with `-unsigned` suffix. + - Upload a signing status metadata file with the release assets. + +## Local development + +- `scripts/get-libft4222.sh install` does not require code signing. +- The script clears macOS download attributes on fetched FTDI files. +- Optional local escape hatch: + - `CHIAVDF_ADHOC_SIGN_FTDI=1 ./scripts/get-libft4222.sh install` diff --git a/scripts/get-libft4222.sh b/scripts/get-libft4222.sh index 0bca8601..d71d51af 100755 --- a/scripts/get-libft4222.sh +++ b/scripts/get-libft4222.sh @@ -102,7 +102,6 @@ install_macos() { need_cmd unzip need_cmd hdiutil need_cmd install_name_tool - need_cmd codesign mkdir -p "$WORK_DIR" fetch_with_retry "$MAC_URL" "$MAC_ARCHIVE" zip 3 @@ -139,14 +138,19 @@ install_macos() { install_name_tool -change "libftd2xx.dylib" "@rpath/libftd2xx.dylib" \ "${WORK_DIR}/libft4222.1.4.4.190.dylib" - # Clear provenance attributes and ad-hoc sign dylibs to avoid execution kills. + # Clear download metadata that can trigger loader trust checks on macOS. if command -v xattr >/dev/null 2>&1; then xattr -dr com.apple.provenance "$WORK_DIR" || true + xattr -dr com.apple.quarantine "$WORK_DIR" || true + fi + # Optional escape hatch for local troubleshooting only. + if [ "${CHIAVDF_ADHOC_SIGN_FTDI:-0}" = "1" ]; then + need_cmd codesign + codesign --force --sign - \ + "${WORK_DIR}/libftd2xx.dylib" \ + "${WORK_DIR}/libft4222.1.4.4.190.dylib" \ + "${WORK_DIR}/libft4222.dylib" fi - codesign --force --sign - \ - "${WORK_DIR}/libftd2xx.dylib" \ - "${WORK_DIR}/libft4222.1.4.4.190.dylib" \ - "${WORK_DIR}/libft4222.dylib" rm -rf "$HW_DIR" ln -s "$WORK_DIR" "$HW_DIR" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 23f1ab6e..87553986 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -474,7 +474,7 @@ if(BUILD_HW_TOOLS) target_link_libraries(hw_test PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads ${HW_LIBS}) vdf_add_windows_clang_opts(hw_test) if(APPLE) - target_link_options(hw_test PRIVATE "-Wl,-rpath,@executable_path/hw/libft4222") + target_link_options(hw_test PRIVATE "-Wl,-rpath,@loader_path/../libexec/chiavdf") endif() add_executable(hw_vdf_client @@ -487,7 +487,7 @@ if(BUILD_HW_TOOLS) target_link_libraries(hw_vdf_client PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads ${HW_LIBS} ${HW_SOCKET_LIBS}) vdf_add_windows_clang_opts(hw_vdf_client) if(APPLE) - target_link_options(hw_vdf_client PRIVATE "-Wl,-rpath,@executable_path/hw/libft4222") + target_link_options(hw_vdf_client PRIVATE "-Wl,-rpath,@loader_path/../libexec/chiavdf") endif() add_executable(emu_hw_test diff --git a/src/Makefile.vdf-client b/src/Makefile.vdf-client index 66c5b93a..088daf26 100644 --- a/src/Makefile.vdf-client +++ b/src/Makefile.vdf-client @@ -114,8 +114,8 @@ endif endif ifeq ($(UNAME),Darwin) -# Ensure HW client binaries can locate USB driver dylibs bundled in-tree. -hw_test hw_vdf_client: LDFLAGS += -Wl,-rpath,@executable_path/hw/libft4222 +# Ensure HW binaries locate bundled FTDI dylibs in release layout. +hw_test hw_vdf_client: LDFLAGS += -Wl,-rpath,@loader_path/../libexec/chiavdf endif hw_test: hw/hw_test.o $(HW_OBJS) $(HW_LIBS) hw/real_hw.o From c65cf5ca1b3b19a9f4ce9a5bf6b10518c663ba11 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Sat, 21 Feb 2026 00:10:33 -0800 Subject: [PATCH 02/21] Sign macOS ARM64 CI bundles whenever Apple secrets are available. Run secret detection, cert import, bundle assembly, and codesign on all optimized macOS ARM64 CI jobs; keep notarization release-only and continue publishing the macOS bundle artifact for non-release runs with run-id versioning. --- .github/workflows/vdf-client-hw.yml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/vdf-client-hw.yml b/.github/workflows/vdf-client-hw.yml index 7db44599..6eab8cdc 100644 --- a/.github/workflows/vdf-client-hw.yml +++ b/.github/workflows/vdf-client-hw.yml @@ -59,7 +59,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Test for Apple signing secrets - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' id: check_secrets shell: bash run: | @@ -70,11 +70,11 @@ jobs: APPLE_SECRET: "${{ secrets.APPLE_DEV_ID_APP }}" - name: Delete keychain if it already exists - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' run: security delete-keychain signing_temp.keychain || true - name: Import Apple app signing certificate - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' uses: Apple-Actions/import-codesign-certs@v6 with: p12-file-base64: ${{ secrets.APPLE_DEV_ID_APP }} @@ -387,14 +387,15 @@ jobs: if ($LASTEXITCODE -ne 0) { throw "vdf_bench failed with exit code $LASTEXITCODE" } - name: Assemble macOS ARM64 brew bundle - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' env: - RELEASE_TAG: ${{ github.event.release.tag_name }} + RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || format('0.0.1-{0}', github.run_id) }} + IS_RELEASE: ${{ github.event_name == 'release' }} HAS_APPLE_SECRET: ${{ steps.check_secrets.outputs.HAS_APPLE_SECRET || '' }} APPLE_NOTARIZE_USERNAME: "${{ secrets.APPLE_NOTARIZE_USERNAME }}" APPLE_NOTARIZE_PASSWORD: "${{ secrets.APPLE_NOTARIZE_PASSWORD }}" APPLE_TEAM_ID: "${{ secrets.APPLE_TEAM_ID }}" - NOTARIZE: ${{ steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' }} + NOTARIZE: ${{ github.event_name == 'release' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' }} run: | set -euo pipefail @@ -441,7 +442,7 @@ jobs: done SIGNING_STATUS="unsigned (no Apple signing secrets available)" - if [ "$HAS_APPLE_SECRET" = "true" ] && [ "${NOTARIZE}" = "true" ]; then + if [ "$HAS_APPLE_SECRET" = "true" ]; then SIGNING_IDENTITY="$(security find-identity -v -p codesigning | rg 'Developer ID Application' | awk -F\" 'NR==1{print $2}')" if [ -z "$SIGNING_IDENTITY" ]; then echo "No Developer ID Application identity found after certificate import." >&2 @@ -456,8 +457,12 @@ jobs: done (cd dist/macos && ditto -c -k --keepParent "${BASE_NAME}" "${ASSET_NAME}") - xcrun notarytool submit "dist/macos/${ASSET_NAME}" --apple-id "$APPLE_NOTARIZE_USERNAME" --password "$APPLE_NOTARIZE_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait - SIGNING_STATUS="signed + notarized" + if [ "${NOTARIZE}" = "true" ]; then + xcrun notarytool submit "dist/macos/${ASSET_NAME}" --apple-id "$APPLE_NOTARIZE_USERNAME" --password "$APPLE_NOTARIZE_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait + SIGNING_STATUS="signed + notarized" + else + SIGNING_STATUS="signed (notarization skipped for non-release run)" + fi else (cd dist/macos && ditto -c -k --keepParent "${BASE_NAME}" "${ASSET_NAME}") fi @@ -466,7 +471,7 @@ jobs: shasum -a 256 "dist/macos/${ASSET_NAME}" | awk '{print $1}' > "dist/macos/${ASSET_NAME}.sha256" - name: Upload macOS ARM64 bundle artifact - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' + if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' uses: actions/upload-artifact@v6 with: name: macos-arm64-brew-bundle From febd372d558d98a2e5788af05e5a0a47e9b1f98b Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Sat, 21 Feb 2026 00:24:29 -0800 Subject: [PATCH 03/21] Use grep for macOS bundle rpath checks in CI. Replace ripgrep-based checks in the macOS ARM64 bundle assembly step with portable grep equivalents so the workflow runs on macOS runners that do not have rg installed by default. --- .github/workflows/vdf-client-hw.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/vdf-client-hw.yml b/.github/workflows/vdf-client-hw.yml index 6eab8cdc..c71c1a6c 100644 --- a/.github/workflows/vdf-client-hw.yml +++ b/.github/workflows/vdf-client-hw.yml @@ -434,8 +434,8 @@ jobs: for exe in "$BIN_DIR/"*; do otool -L "$exe" - otool -l "$exe" | rg "@loader_path/../libexec/chiavdf" - if otool -L "$exe" | rg -q "${GITHUB_WORKSPACE}|/Users/|/private/var/folders"; then + otool -l "$exe" | grep -F "@loader_path/../libexec/chiavdf" + if otool -L "$exe" | grep -Eq "${GITHUB_WORKSPACE}|/Users/|/private/var/folders"; then echo "Found non-portable absolute library path in ${exe}" >&2 exit 1 fi From a0280dbf0b09812aa8b55f224f83000a657fd544 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Sat, 21 Feb 2026 00:35:25 -0800 Subject: [PATCH 04/21] Update macOS hardware client rpath and clean workflow env vars --- .github/workflows/vdf-client-hw.yml | 2 -- src/CMakeLists.txt | 4 ++-- src/Makefile.vdf-client | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/vdf-client-hw.yml b/.github/workflows/vdf-client-hw.yml index c71c1a6c..38c772ca 100644 --- a/.github/workflows/vdf-client-hw.yml +++ b/.github/workflows/vdf-client-hw.yml @@ -390,7 +390,6 @@ jobs: if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' env: RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || format('0.0.1-{0}', github.run_id) }} - IS_RELEASE: ${{ github.event_name == 'release' }} HAS_APPLE_SECRET: ${{ steps.check_secrets.outputs.HAS_APPLE_SECRET || '' }} APPLE_NOTARIZE_USERNAME: "${{ secrets.APPLE_NOTARIZE_USERNAME }}" APPLE_NOTARIZE_PASSWORD: "${{ secrets.APPLE_NOTARIZE_PASSWORD }}" @@ -520,7 +519,6 @@ jobs: - name: Assemble Ubuntu .deb (same runner as build) if: matrix.os == 'ubuntu-latest' && matrix.config == 'optimized=1' env: - RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || '' }} INSTALLER_VERSION: "${{ github.event_name == 'release' && github.event.release.tag_name || format('0.0.1-{0}', github.run_id) }}" PLATFORM: "amd64" run: | diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 87553986..23f1ab6e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -474,7 +474,7 @@ if(BUILD_HW_TOOLS) target_link_libraries(hw_test PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads ${HW_LIBS}) vdf_add_windows_clang_opts(hw_test) if(APPLE) - target_link_options(hw_test PRIVATE "-Wl,-rpath,@loader_path/../libexec/chiavdf") + target_link_options(hw_test PRIVATE "-Wl,-rpath,@executable_path/hw/libft4222") endif() add_executable(hw_vdf_client @@ -487,7 +487,7 @@ if(BUILD_HW_TOOLS) target_link_libraries(hw_vdf_client PRIVATE ${GMP_LIBRARIES} ${GMPXX_LIBRARIES} Threads::Threads ${HW_LIBS} ${HW_SOCKET_LIBS}) vdf_add_windows_clang_opts(hw_vdf_client) if(APPLE) - target_link_options(hw_vdf_client PRIVATE "-Wl,-rpath,@loader_path/../libexec/chiavdf") + target_link_options(hw_vdf_client PRIVATE "-Wl,-rpath,@executable_path/hw/libft4222") endif() add_executable(emu_hw_test diff --git a/src/Makefile.vdf-client b/src/Makefile.vdf-client index 088daf26..66c5b93a 100644 --- a/src/Makefile.vdf-client +++ b/src/Makefile.vdf-client @@ -114,8 +114,8 @@ endif endif ifeq ($(UNAME),Darwin) -# Ensure HW binaries locate bundled FTDI dylibs in release layout. -hw_test hw_vdf_client: LDFLAGS += -Wl,-rpath,@loader_path/../libexec/chiavdf +# Ensure HW client binaries can locate USB driver dylibs bundled in-tree. +hw_test hw_vdf_client: LDFLAGS += -Wl,-rpath,@executable_path/hw/libft4222 endif hw_test: hw/hw_test.o $(HW_OBJS) $(HW_LIBS) hw/real_hw.o From 7c63b0649ebd7c12bd2e1eabbc89063a6c0e1f64 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Sat, 21 Feb 2026 00:38:03 -0800 Subject: [PATCH 05/21] Clarify macOS bundle rpath rewrite during assembly --- assets/macos-release-bundle.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/macos-release-bundle.md b/assets/macos-release-bundle.md index a2198170..f8c71d8b 100644 --- a/assets/macos-release-bundle.md +++ b/assets/macos-release-bundle.md @@ -18,7 +18,8 @@ This repository publishes a macOS ARM64 release archive intended for Homebrew/ca ## Dynamic library path policy -- Hardware binaries are linked with `@loader_path/../libexec/chiavdf` rpath on macOS. +- Hardware binaries may be built with a development-time rpath, then are rewritten during bundle assembly. +- The bundle assembly step removes development-time rpaths and sets `@loader_path/../libexec/chiavdf` on macOS. - FTDI dylibs use `@rpath` install names. - CI verifies with `otool` that release binaries do not reference local absolute build paths. From e739737105758b06716573afbd0d002dd7f9dc48 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Sat, 21 Feb 2026 00:54:01 -0800 Subject: [PATCH 06/21] Avoid re-signing libft4222 symlink in macOS bundle workflow --- .github/workflows/vdf-client-hw.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/vdf-client-hw.yml b/.github/workflows/vdf-client-hw.yml index 38c772ca..0f8b501e 100644 --- a/.github/workflows/vdf-client-hw.yml +++ b/.github/workflows/vdf-client-hw.yml @@ -450,7 +450,6 @@ jobs: codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$LIBEXEC_DIR/libftd2xx.dylib" codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$LIBEXEC_DIR/libft4222.1.4.4.190.dylib" - codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$LIBEXEC_DIR/libft4222.dylib" for exe in "$BIN_DIR/"*; do codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$exe" done From 73e4ad89e630cccffd4b14ccbaebfed54f9f7b1d Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Sat, 21 Feb 2026 01:38:00 -0800 Subject: [PATCH 07/21] fix(ci): replace rg with grep in macOS signing step to avoid runner failure --- .github/workflows/vdf-client-hw.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vdf-client-hw.yml b/.github/workflows/vdf-client-hw.yml index 0f8b501e..96097161 100644 --- a/.github/workflows/vdf-client-hw.yml +++ b/.github/workflows/vdf-client-hw.yml @@ -442,7 +442,7 @@ jobs: SIGNING_STATUS="unsigned (no Apple signing secrets available)" if [ "$HAS_APPLE_SECRET" = "true" ]; then - SIGNING_IDENTITY="$(security find-identity -v -p codesigning | rg 'Developer ID Application' | awk -F\" 'NR==1{print $2}')" + SIGNING_IDENTITY="$(security find-identity -v -p codesigning | grep 'Developer ID Application' | awk -F\" 'NR==1{print $2}')" if [ -z "$SIGNING_IDENTITY" ]; then echo "No Developer ID Application identity found after certificate import." >&2 exit 1 From d975493f35bde7d6c826b4d0a6165ffb9053c462 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Sat, 21 Feb 2026 22:36:59 -0800 Subject: [PATCH 08/21] make macOS arm64 GMP linking explicitly static in CI builds Add a STATIC_GMP make toggle and wire macos-13-arm64 workflow builds to pass static GMP archives so release/local CI binaries avoid dynamic Homebrew GMP runtime dependencies. --- .github/workflows/vdf-client-hw.yml | 14 +++++++++++--- src/Makefile.vdf-client | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vdf-client-hw.yml b/.github/workflows/vdf-client-hw.yml index 96097161..f9cc389a 100644 --- a/.github/workflows/vdf-client-hw.yml +++ b/.github/workflows/vdf-client-hw.yml @@ -86,7 +86,10 @@ jobs: brew ls --versions cmake >/dev/null 2>&1 || brew install cmake brew ls --versions gmp >/dev/null 2>&1 || brew install gmp brew ls --versions boost >/dev/null 2>&1 || brew install boost - echo "DYLD_FALLBACK_LIBRARY_PATH=$(brew --prefix gmp)/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}" >> "$GITHUB_ENV" + # Keep dynamic GMP runtime lookup for non-arm64 jobs. + if [ "${{ matrix.os }}" != "macos-13-arm64" ]; then + echo "DYLD_FALLBACK_LIBRARY_PATH=$(brew --prefix gmp)/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}" >> "$GITHUB_ENV" + fi # Runtime workaround for TSAN-instrumented binaries on Ubuntu runners. # Apply before build because make TSAN=1 runs ./compile_asm during build. @@ -115,6 +118,11 @@ jobs: if: startsWith(matrix.os, 'macos') run: | BOOST_VERSION="$(brew list --versions boost | awk '{print $2}')" + GMP_PREFIX="$(brew --prefix gmp)" + make_gmp_args=() + if [ "${{ matrix.os }}" = "macos-13-arm64" ]; then + make_gmp_args+=(STATIC_GMP=1 "GMP_PREFIX=${GMP_PREFIX}") + fi BOOST_INCLUDE="" for cand in \ "$(brew --prefix boost)/include" \ @@ -135,9 +143,9 @@ jobs: ./scripts/get-libft4222.sh install cd src if [ -n "$RELEASE_TAG" ]; then - make ${{ matrix.config }} CHIAVDF_VERSION="$RELEASE_TAG" CPPFLAGS="-I${BOOST_INCLUDE} ${CPPFLAGS:-}" -f Makefile.vdf-client vdf_client vdf_bench 1weso_test 2weso_test prover_test emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client + make ${{ matrix.config }} "${make_gmp_args[@]}" CHIAVDF_VERSION="$RELEASE_TAG" CPPFLAGS="-I${BOOST_INCLUDE} ${CPPFLAGS:-}" -f Makefile.vdf-client vdf_client vdf_bench 1weso_test 2weso_test prover_test emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client else - make ${{ matrix.config }} CPPFLAGS="-I${BOOST_INCLUDE} ${CPPFLAGS:-}" -f Makefile.vdf-client vdf_client vdf_bench 1weso_test 2weso_test prover_test emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client + make ${{ matrix.config }} "${make_gmp_args[@]}" CPPFLAGS="-I${BOOST_INCLUDE} ${CPPFLAGS:-}" -f Makefile.vdf-client vdf_client vdf_bench 1weso_test 2weso_test prover_test emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client fi - name: Cache Boost on Windows diff --git a/src/Makefile.vdf-client b/src/Makefile.vdf-client index 66c5b93a..ad1c9563 100644 --- a/src/Makefile.vdf-client +++ b/src/Makefile.vdf-client @@ -2,6 +2,7 @@ UNAME := $(shell uname) ARCH := $(shell uname -m) GIT_DESCRIBE := $(strip $(shell git describe --tags --always 2>/dev/null)) CHIAVDF_VERSION ?= $(if $(GIT_DESCRIBE),$(GIT_DESCRIBE),dev) +STATIC_GMP = 0 ifneq (,$(findstring clang, $(shell $(CXX) --version))) NOPIE = -fno-PIE @@ -41,6 +42,21 @@ CXXFLAGS += -I/usr/local/include CFLAGS += -I/usr/local/include LDFLAGS += -L/usr/local/lib endif +ifeq ($(STATIC_GMP),1) +# On macOS ARM64 distribution/local builds, prefer static GMP for portability. +GMP_PREFIX ?= $(shell brew --prefix gmp 2>/dev/null) +ifeq ($(GMP_PREFIX),) +$(error STATIC_GMP=1 requires GMP_PREFIX or a discoverable Homebrew gmp prefix) +endif +ifeq ($(wildcard $(GMP_PREFIX)/lib/libgmp.a),) +$(error Static GMP archive not found at $(GMP_PREFIX)/lib/libgmp.a) +endif +ifeq ($(wildcard $(GMP_PREFIX)/lib/libgmpxx.a),) +$(error Static GMPXX archive not found at $(GMP_PREFIX)/lib/libgmpxx.a) +endif +LDLIBS := $(filter-out -lgmpxx -lgmp,$(LDLIBS)) +LDLIBS += $(GMP_PREFIX)/lib/libgmpxx.a $(GMP_PREFIX)/lib/libgmp.a +endif endif CXXFLAGS += -DCHIAVDF_VERSION=\"$(CHIAVDF_VERSION)\" From 6c8015e5bb01266c2504033d282ff596892bda0a Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Sat, 21 Feb 2026 22:44:53 -0800 Subject: [PATCH 09/21] Revert static macOS GMP linking in CI and add prerequisite reminder. This restores dynamic GMP handling and leaves a TODO to make GMP a formal prerequisite once the Homebrew cask/tap wiring is in place. Co-authored-by: Cursor --- .github/workflows/vdf-client-hw.yml | 15 ++++----------- src/Makefile.vdf-client | 16 ---------------- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/.github/workflows/vdf-client-hw.yml b/.github/workflows/vdf-client-hw.yml index f9cc389a..af7f7e63 100644 --- a/.github/workflows/vdf-client-hw.yml +++ b/.github/workflows/vdf-client-hw.yml @@ -84,12 +84,10 @@ jobs: if: startsWith(matrix.os, 'macos') run: | brew ls --versions cmake >/dev/null 2>&1 || brew install cmake + # TODO: Make gmp a prerequisite once brew cask/tap wiring is in place. brew ls --versions gmp >/dev/null 2>&1 || brew install gmp brew ls --versions boost >/dev/null 2>&1 || brew install boost - # Keep dynamic GMP runtime lookup for non-arm64 jobs. - if [ "${{ matrix.os }}" != "macos-13-arm64" ]; then - echo "DYLD_FALLBACK_LIBRARY_PATH=$(brew --prefix gmp)/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}" >> "$GITHUB_ENV" - fi + echo "DYLD_FALLBACK_LIBRARY_PATH=$(brew --prefix gmp)/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}" >> "$GITHUB_ENV" # Runtime workaround for TSAN-instrumented binaries on Ubuntu runners. # Apply before build because make TSAN=1 runs ./compile_asm during build. @@ -118,11 +116,6 @@ jobs: if: startsWith(matrix.os, 'macos') run: | BOOST_VERSION="$(brew list --versions boost | awk '{print $2}')" - GMP_PREFIX="$(brew --prefix gmp)" - make_gmp_args=() - if [ "${{ matrix.os }}" = "macos-13-arm64" ]; then - make_gmp_args+=(STATIC_GMP=1 "GMP_PREFIX=${GMP_PREFIX}") - fi BOOST_INCLUDE="" for cand in \ "$(brew --prefix boost)/include" \ @@ -143,9 +136,9 @@ jobs: ./scripts/get-libft4222.sh install cd src if [ -n "$RELEASE_TAG" ]; then - make ${{ matrix.config }} "${make_gmp_args[@]}" CHIAVDF_VERSION="$RELEASE_TAG" CPPFLAGS="-I${BOOST_INCLUDE} ${CPPFLAGS:-}" -f Makefile.vdf-client vdf_client vdf_bench 1weso_test 2weso_test prover_test emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client + make ${{ matrix.config }} CHIAVDF_VERSION="$RELEASE_TAG" CPPFLAGS="-I${BOOST_INCLUDE} ${CPPFLAGS:-}" -f Makefile.vdf-client vdf_client vdf_bench 1weso_test 2weso_test prover_test emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client else - make ${{ matrix.config }} "${make_gmp_args[@]}" CPPFLAGS="-I${BOOST_INCLUDE} ${CPPFLAGS:-}" -f Makefile.vdf-client vdf_client vdf_bench 1weso_test 2weso_test prover_test emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client + make ${{ matrix.config }} CPPFLAGS="-I${BOOST_INCLUDE} ${CPPFLAGS:-}" -f Makefile.vdf-client vdf_client vdf_bench 1weso_test 2weso_test prover_test emu_hw_test hw_test emu_hw_vdf_client hw_vdf_client fi - name: Cache Boost on Windows diff --git a/src/Makefile.vdf-client b/src/Makefile.vdf-client index ad1c9563..66c5b93a 100644 --- a/src/Makefile.vdf-client +++ b/src/Makefile.vdf-client @@ -2,7 +2,6 @@ UNAME := $(shell uname) ARCH := $(shell uname -m) GIT_DESCRIBE := $(strip $(shell git describe --tags --always 2>/dev/null)) CHIAVDF_VERSION ?= $(if $(GIT_DESCRIBE),$(GIT_DESCRIBE),dev) -STATIC_GMP = 0 ifneq (,$(findstring clang, $(shell $(CXX) --version))) NOPIE = -fno-PIE @@ -42,21 +41,6 @@ CXXFLAGS += -I/usr/local/include CFLAGS += -I/usr/local/include LDFLAGS += -L/usr/local/lib endif -ifeq ($(STATIC_GMP),1) -# On macOS ARM64 distribution/local builds, prefer static GMP for portability. -GMP_PREFIX ?= $(shell brew --prefix gmp 2>/dev/null) -ifeq ($(GMP_PREFIX),) -$(error STATIC_GMP=1 requires GMP_PREFIX or a discoverable Homebrew gmp prefix) -endif -ifeq ($(wildcard $(GMP_PREFIX)/lib/libgmp.a),) -$(error Static GMP archive not found at $(GMP_PREFIX)/lib/libgmp.a) -endif -ifeq ($(wildcard $(GMP_PREFIX)/lib/libgmpxx.a),) -$(error Static GMPXX archive not found at $(GMP_PREFIX)/lib/libgmpxx.a) -endif -LDLIBS := $(filter-out -lgmpxx -lgmp,$(LDLIBS)) -LDLIBS += $(GMP_PREFIX)/lib/libgmpxx.a $(GMP_PREFIX)/lib/libgmp.a -endif endif CXXFLAGS += -DCHIAVDF_VERSION=\"$(CHIAVDF_VERSION)\" From 0b753bd16955313b857f09f6bf2fdbb709429980 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 16:13:32 -0800 Subject: [PATCH 10/21] fix(ci): make macOS ARM64 bundle rpath updates conditional Avoid failing/noisy install_name_tool delete_rpath calls by changing rpaths only when present or missing, keeping bundle assembly logs clean while preserving portable loader paths. Co-authored-by: Cursor --- .github/workflows/vdf-client-hw.yml | 36 +++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/.github/workflows/vdf-client-hw.yml b/.github/workflows/vdf-client-hw.yml index 38c06b85..60c3c4d8 100644 --- a/.github/workflows/vdf-client-hw.yml +++ b/.github/workflows/vdf-client-hw.yml @@ -64,10 +64,18 @@ jobs: shell: bash run: | unset HAS_APPLE_SECRET + unset HAS_NOTARIZE_SECRETS if [ -n "$APPLE_SECRET" ]; then HAS_APPLE_SECRET='true'; fi + if [ -n "$APPLE_NOTARIZE_USERNAME" ] && [ -n "$APPLE_NOTARIZE_PASSWORD" ] && [ -n "$APPLE_TEAM_ID" ]; then + HAS_NOTARIZE_SECRETS='true' + fi echo "HAS_APPLE_SECRET=${HAS_APPLE_SECRET}" >> "$GITHUB_OUTPUT" + echo "HAS_NOTARIZE_SECRETS=${HAS_NOTARIZE_SECRETS}" >> "$GITHUB_OUTPUT" env: APPLE_SECRET: "${{ secrets.APPLE_DEV_ID_APP }}" + APPLE_NOTARIZE_USERNAME: "${{ secrets.APPLE_NOTARIZE_USERNAME }}" + APPLE_NOTARIZE_PASSWORD: "${{ secrets.APPLE_NOTARIZE_PASSWORD }}" + APPLE_TEAM_ID: "${{ secrets.APPLE_TEAM_ID }}" - name: Delete keychain if it already exists if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' @@ -395,7 +403,7 @@ jobs: APPLE_NOTARIZE_USERNAME: "${{ secrets.APPLE_NOTARIZE_USERNAME }}" APPLE_NOTARIZE_PASSWORD: "${{ secrets.APPLE_NOTARIZE_PASSWORD }}" APPLE_TEAM_ID: "${{ secrets.APPLE_TEAM_ID }}" - NOTARIZE: ${{ github.event_name == 'release' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' }} + NOTARIZE: ${{ github.event_name == 'release' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' && steps.check_secrets.outputs.HAS_NOTARIZE_SECRETS == 'true' }} run: | set -euo pipefail @@ -422,14 +430,19 @@ jobs: cp src/hw/libft4222/libft4222.1.4.4.190.dylib "$LIBEXEC_DIR/" ln -sf "libft4222.1.4.4.190.dylib" "$LIBEXEC_DIR/libft4222.dylib" - install_name_tool -id "@rpath/libftd2xx.dylib" "$LIBEXEC_DIR/libftd2xx.dylib" - install_name_tool -id "@rpath/libft4222.dylib" "$LIBEXEC_DIR/libft4222.1.4.4.190.dylib" - install_name_tool -change "libftd2xx.dylib" "@rpath/libftd2xx.dylib" "$LIBEXEC_DIR/libft4222.1.4.4.190.dylib" + # Keep vendor libft4222 dylibs byte-identical so existing signatures remain intact. for exe in "$BIN_DIR/"*; do - install_name_tool -delete_rpath "@executable_path/hw/libft4222" "$exe" || true - install_name_tool -delete_rpath "@loader_path/../libexec/chiavdf" "$exe" || true - install_name_tool -add_rpath "@loader_path/../libexec/chiavdf" "$exe" + EXE_RPATHS="$(otool -l "$exe" | awk ' + $1 == "cmd" && $2 == "LC_RPATH" { in_rpath = 1; next } + in_rpath && $1 == "path" { print $2; in_rpath = 0 } + ')" + if printf '%s\n' "$EXE_RPATHS" | grep -Fxq "@executable_path/hw/libft4222"; then + install_name_tool -delete_rpath "@executable_path/hw/libft4222" "$exe" + fi + if ! printf '%s\n' "$EXE_RPATHS" | grep -Fxq "@loader_path/../libexec/chiavdf"; then + install_name_tool -add_rpath "@loader_path/../libexec/chiavdf" "$exe" + fi done for exe in "$BIN_DIR/"*; do @@ -439,6 +452,13 @@ jobs: echo "Found non-portable absolute library path in ${exe}" >&2 exit 1 fi + if otool -l "$exe" | awk ' + $1 == "cmd" && $2 == "LC_RPATH" { in_rpath = 1; next } + in_rpath && $1 == "path" { print $2; in_rpath = 0 } + ' | grep -Eq "^(${GITHUB_WORKSPACE}|/Users/|/private/var/folders)"; then + echo "Found non-portable absolute rpath in ${exe}" >&2 + exit 1 + fi done SIGNING_STATUS="unsigned (no Apple signing secrets available)" @@ -449,8 +469,6 @@ jobs: exit 1 fi - codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$LIBEXEC_DIR/libftd2xx.dylib" - codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$LIBEXEC_DIR/libft4222.1.4.4.190.dylib" for exe in "$BIN_DIR/"*; do codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$exe" done From 102161db4b3a8fc12ed70725fae2b5c27b6b03e7 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 16:35:39 -0800 Subject: [PATCH 11/21] fix(ci): sign macOS FTDI dylibs and emit codesign audit summary Ensure the macOS ARM64 bundle signs both executables and bundled FTDI dylibs, verifies all shipped Mach-O artifacts carry valid Developer ID signatures, and publishes a codesign -dv summary artifact for release auditing. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 62 ++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 22a92255..00a3404b 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -430,7 +430,17 @@ jobs: cp src/hw/libft4222/libft4222.1.4.4.190.dylib "$LIBEXEC_DIR/" ln -sf "libft4222.1.4.4.190.dylib" "$LIBEXEC_DIR/libft4222.dylib" - # Keep vendor libft4222 dylibs byte-identical so existing signatures remain intact. + dylib_files=( + "$LIBEXEC_DIR/libftd2xx.dylib" + "$LIBEXEC_DIR/libft4222.1.4.4.190.dylib" + ) + sign_targets=() + for exe in "$BIN_DIR/"*; do + sign_targets+=("$exe") + done + for dylib in "${dylib_files[@]}"; do + sign_targets+=("$dylib") + done for exe in "$BIN_DIR/"*; do EXE_RPATHS="$(otool -l "$exe" | awk ' @@ -462,6 +472,7 @@ jobs: done SIGNING_STATUS="unsigned (no Apple signing secrets available)" + CODESIGN_SUMMARY_PATH="dist/macos/${BASE_NAME}.codesign-summary.txt" if [ "$HAS_APPLE_SECRET" = "true" ]; then SIGNING_IDENTITY="$(security find-identity -v -p codesigning | grep 'Developer ID Application' | awk -F\" 'NR==1{print $2}')" if [ -z "$SIGNING_IDENTITY" ]; then @@ -469,8 +480,49 @@ jobs: exit 1 fi - for exe in "$BIN_DIR/"*; do - codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$exe" + is_valid_dev_id_signature() { + local path="$1" + if ! codesign --verify --strict --verbose=2 "$path" >/dev/null 2>&1; then + return 1 + fi + if ! codesign -dv "$path" 2>&1 | grep -Fq "Authority=Developer ID Application"; then + return 1 + fi + return 0 + } + + echo "Checking FTDI dylib signatures before signing..." + for dylib in "${dylib_files[@]}"; do + if is_valid_dev_id_signature "$dylib"; then + echo "FTDI dylib already has a valid Developer ID signature: $dylib" + else + echo "FTDI dylib needs signing: $dylib" + fi + done + + for target in "${sign_targets[@]}"; do + if is_valid_dev_id_signature "$target"; then + echo "Already signed with Developer ID: $target" + continue + fi + codesign --force --timestamp --options runtime --sign "$SIGNING_IDENTITY" "$target" + done + + echo "Verifying all bundled Mach-O binaries are signed..." + for target in "${sign_targets[@]}"; do + if ! is_valid_dev_id_signature "$target"; then + echo "Unsigned or invalid Developer ID signature: $target" >&2 + codesign -dv "$target" 2>&1 || true + exit 1 + fi + done + : > "$CODESIGN_SUMMARY_PATH" + printf "codesign -dv summary for %s\n\n" "$BASE_NAME" >> "$CODESIGN_SUMMARY_PATH" + echo "codesign -dv summary (post-sign verification):" + for target in "${sign_targets[@]}"; do + printf "==== %s ====\n" "$target" | tee -a "$CODESIGN_SUMMARY_PATH" + codesign -dv "$target" 2>&1 | grep -E '^(Identifier=|Format=|CodeDirectory v=|Authority=|TeamIdentifier=|Timestamp=)' | tee -a "$CODESIGN_SUMMARY_PATH" + echo | tee -a "$CODESIGN_SUMMARY_PATH" done (cd dist/macos && ditto -c -k --keepParent "${BASE_NAME}" "${ASSET_NAME}") @@ -496,6 +548,7 @@ jobs: dist/macos/*.zip dist/macos/*.sha256 dist/macos/*.signing-status.txt + dist/macos/*.codesign-summary.txt - name: Upload binaries artifact (Ubuntu) if: startsWith(matrix.os, 'ubuntu') && matrix.config == 'optimized=1' @@ -604,7 +657,8 @@ jobs: $RELEASE_TAG \ dist/macos/*.zip \ dist/macos/*.sha256 \ - dist/macos/*.signing-status.txt + dist/macos/*.signing-status.txt \ + dist/macos/*.codesign-summary.txt trigger-repo-update: name: Trigger repo update From 9f7e79ca225fe2ecd0d53ab9e7ce90032c1e3852 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 16:39:36 -0800 Subject: [PATCH 12/21] fix(ci): handle optional macOS codesign artifact upload Avoid passing unmatched macOS codesign-summary globs to gh release upload so unsigned release builds can still publish artifacts. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 00a3404b..31b53b1d 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -652,13 +652,21 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_TAG: ${{ github.event.release.tag_name }} run: | + shopt -s nullglob + release_files=( + dist/macos/*.zip + dist/macos/*.sha256 + dist/macos/*.signing-status.txt + dist/macos/*.codesign-summary.txt + ) + if [ "${#release_files[@]}" -eq 0 ]; then + echo "No macOS release artifacts found to upload" >&2 + exit 1 + fi gh release upload \ --clobber \ - $RELEASE_TAG \ - dist/macos/*.zip \ - dist/macos/*.sha256 \ - dist/macos/*.signing-status.txt \ - dist/macos/*.codesign-summary.txt + "$RELEASE_TAG" \ + "${release_files[@]}" trigger-repo-update: name: Trigger repo update From 600561e80ce2fb78ea31ddf105507f6da1beb306 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 16:48:10 -0800 Subject: [PATCH 13/21] fix(ci): relax macOS Developer ID verification fallback Allow macOS bundle signature verification to pass when codesign output omits Authority lines but strict verification succeeds and TeamIdentifier is present. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 31b53b1d..7ee23434 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -482,13 +482,21 @@ jobs: is_valid_dev_id_signature() { local path="$1" + local details if ! codesign --verify --strict --verbose=2 "$path" >/dev/null 2>&1; then return 1 fi - if ! codesign -dv "$path" 2>&1 | grep -Fq "Authority=Developer ID Application"; then - return 1 + details="$(codesign -dv "$path" 2>&1 || true)" + if printf '%s\n' "$details" | grep -Fq "Authority=Developer ID Application"; then + return 0 + fi + # Some runners may omit Authority lines in codesign -dv output. + # Treat a strict-valid signature with a populated TeamIdentifier + # as acceptable for Developer ID verification. + if printf '%s\n' "$details" | grep -Eq '^TeamIdentifier=[^[:space:]]+' && ! printf '%s\n' "$details" | grep -Fq 'TeamIdentifier=not set'; then + return 0 fi - return 0 + return 1 } echo "Checking FTDI dylib signatures before signing..." From dcc90f348626c5db249f2d8662c71965af7167a0 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 16:54:56 -0800 Subject: [PATCH 14/21] fix(ci): publish macOS Intel brew bundle artifacts Run the macOS bundle/sign/upload flow for both Intel and ARM64 so release and CI outputs include both brew-tap distributables. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 29 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 7ee23434..a8a29c05 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -59,7 +59,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Test for Apple signing secrets - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' + if: startsWith(matrix.os, 'macos') && matrix.config == 'optimized=1' id: check_secrets shell: bash run: | @@ -78,11 +78,11 @@ jobs: APPLE_TEAM_ID: "${{ secrets.APPLE_TEAM_ID }}" - name: Delete keychain if it already exists - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' + if: startsWith(matrix.os, 'macos') && matrix.config == 'optimized=1' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' run: security delete-keychain signing_temp.keychain || true - name: Import Apple app signing certificate - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' + if: startsWith(matrix.os, 'macos') && matrix.config == 'optimized=1' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' uses: Apple-Actions/import-codesign-certs@v6 with: p12-file-base64: ${{ secrets.APPLE_DEV_ID_APP }} @@ -395,8 +395,8 @@ jobs: .\vdf_bench.exe square_asm 2000000 if ($LASTEXITCODE -ne 0) { throw "vdf_bench failed with exit code $LASTEXITCODE" } - - name: Assemble macOS ARM64 brew bundle - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' + - name: Assemble macOS brew bundle + if: startsWith(matrix.os, 'macos') && matrix.config == 'optimized=1' env: RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || format('0.0.1-{0}', github.run_id) }} HAS_APPLE_SECRET: ${{ steps.check_secrets.outputs.HAS_APPLE_SECRET || '' }} @@ -407,7 +407,12 @@ jobs: run: | set -euo pipefail - BASE_NAME="chiavdf-${RELEASE_TAG}-macos-arm64" + if [ "${{ matrix.os }}" = "macos-13-arm64" ]; then + MACOS_ARCH="arm64" + else + MACOS_ARCH="intel" + fi + BASE_NAME="chiavdf-${RELEASE_TAG}-macos-${MACOS_ARCH}" if [ "$HAS_APPLE_SECRET" = "true" ]; then ASSET_NAME="${BASE_NAME}.zip" else @@ -544,14 +549,14 @@ jobs: (cd dist/macos && ditto -c -k --keepParent "${BASE_NAME}" "${ASSET_NAME}") fi - printf "macOS ARM64 release bundle status: %s\n" "$SIGNING_STATUS" > "dist/macos/${BASE_NAME}.signing-status.txt" + printf "macOS %s release bundle status: %s\n" "$MACOS_ARCH" "$SIGNING_STATUS" > "dist/macos/${BASE_NAME}.signing-status.txt" shasum -a 256 "dist/macos/${ASSET_NAME}" | awk '{print $1}' > "dist/macos/${ASSET_NAME}.sha256" - - name: Upload macOS ARM64 bundle artifact - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' + - name: Upload macOS brew bundle artifact + if: startsWith(matrix.os, 'macos') && matrix.config == 'optimized=1' uses: actions/upload-artifact@v6 with: - name: macos-arm64-brew-bundle + name: ${{ matrix.os == 'macos-13-arm64' && 'macos-arm64-brew-bundle' || 'macos-intel-brew-bundle' }} path: | dist/macos/*.zip dist/macos/*.sha256 @@ -654,8 +659,8 @@ jobs: $RELEASE_TAG \ dist/*.deb - - name: Upload macOS ARM64 release artifacts - if: matrix.os == 'macos-13-arm64' && matrix.config == 'optimized=1' && github.event_name == 'release' + - name: Upload macOS release artifacts + if: startsWith(matrix.os, 'macos') && matrix.config == 'optimized=1' && github.event_name == 'release' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_TAG: ${{ github.event.release.tag_name }} From 4dac080a6c35fb7cfa051a19fadf01b34cc491ce Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 17:11:54 -0800 Subject: [PATCH 15/21] fix(ci): require notarized macOS release bundles Enforce release-time checks for Apple signing and notarization secrets so macOS artifacts fail fast unless they are signed and notarized, while keeping non-release runs permissive. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index a8a29c05..4105427c 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -399,7 +399,9 @@ jobs: if: startsWith(matrix.os, 'macos') && matrix.config == 'optimized=1' env: RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || format('0.0.1-{0}', github.run_id) }} + IS_RELEASE: ${{ github.event_name == 'release' && 'true' || 'false' }} HAS_APPLE_SECRET: ${{ steps.check_secrets.outputs.HAS_APPLE_SECRET || '' }} + HAS_NOTARIZE_SECRETS: ${{ steps.check_secrets.outputs.HAS_NOTARIZE_SECRETS || '' }} APPLE_NOTARIZE_USERNAME: "${{ secrets.APPLE_NOTARIZE_USERNAME }}" APPLE_NOTARIZE_PASSWORD: "${{ secrets.APPLE_NOTARIZE_PASSWORD }}" APPLE_TEAM_ID: "${{ secrets.APPLE_TEAM_ID }}" @@ -413,7 +415,15 @@ jobs: MACOS_ARCH="intel" fi BASE_NAME="chiavdf-${RELEASE_TAG}-macos-${MACOS_ARCH}" - if [ "$HAS_APPLE_SECRET" = "true" ]; then + if [ "$IS_RELEASE" = "true" ] && [ "$HAS_APPLE_SECRET" != "true" ]; then + echo "Release build requires Apple signing certificate secrets (APPLE_DEV_ID_APP/APPLE_DEV_ID_APP_PASS)." >&2 + exit 1 + fi + if [ "$IS_RELEASE" = "true" ] && [ "$HAS_NOTARIZE_SECRETS" != "true" ]; then + echo "Release build requires notarization secrets (APPLE_NOTARIZE_USERNAME/APPLE_NOTARIZE_PASSWORD/APPLE_TEAM_ID)." >&2 + exit 1 + fi + if [ "$HAS_APPLE_SECRET" = "true" ] || [ "$IS_RELEASE" = "true" ]; then ASSET_NAME="${BASE_NAME}.zip" else ASSET_NAME="${BASE_NAME}-unsigned.zip" @@ -540,12 +550,23 @@ jobs: (cd dist/macos && ditto -c -k --keepParent "${BASE_NAME}" "${ASSET_NAME}") if [ "${NOTARIZE}" = "true" ]; then - xcrun notarytool submit "dist/macos/${ASSET_NAME}" --apple-id "$APPLE_NOTARIZE_USERNAME" --password "$APPLE_NOTARIZE_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait + if ! xcrun notarytool submit "dist/macos/${ASSET_NAME}" --apple-id "$APPLE_NOTARIZE_USERNAME" --password "$APPLE_NOTARIZE_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait; then + echo "Notarization failed for dist/macos/${ASSET_NAME}" >&2 + exit 1 + fi SIGNING_STATUS="signed + notarized" else + if [ "$IS_RELEASE" = "true" ]; then + echo "Release build reached unexpected state: notarization was not enabled." >&2 + exit 1 + fi SIGNING_STATUS="signed (notarization skipped for non-release run)" fi else + if [ "$IS_RELEASE" = "true" ]; then + echo "Release build reached unexpected state: bundle was not signed." >&2 + exit 1 + fi (cd dist/macos && ditto -c -k --keepParent "${BASE_NAME}" "${ASSET_NAME}") fi From 043ec60e0e50cc54bbdc888f910ac325dfb131c9 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 17:24:42 -0800 Subject: [PATCH 16/21] fix(ci): allow workflow_dispatch macOS notarization requests Add a workflow_dispatch input to explicitly request macOS notarization and route the existing checks/gates through that request path so manual runs can exercise notarization behavior. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 4105427c..79e874b4 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -10,6 +10,12 @@ on: branches: - '**' workflow_dispatch: + inputs: + macos_notarize: + description: "Notarize macOS bundles (requires Apple signing + notarization secrets)" + required: false + default: false + type: boolean concurrency: # SHA is added to the end if on `main` to let all main workflows run @@ -400,12 +406,13 @@ jobs: env: RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || format('0.0.1-{0}', github.run_id) }} IS_RELEASE: ${{ github.event_name == 'release' && 'true' || 'false' }} + REQUEST_NOTARIZE: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.macos_notarize == 'true') }} HAS_APPLE_SECRET: ${{ steps.check_secrets.outputs.HAS_APPLE_SECRET || '' }} HAS_NOTARIZE_SECRETS: ${{ steps.check_secrets.outputs.HAS_NOTARIZE_SECRETS || '' }} APPLE_NOTARIZE_USERNAME: "${{ secrets.APPLE_NOTARIZE_USERNAME }}" APPLE_NOTARIZE_PASSWORD: "${{ secrets.APPLE_NOTARIZE_PASSWORD }}" APPLE_TEAM_ID: "${{ secrets.APPLE_TEAM_ID }}" - NOTARIZE: ${{ github.event_name == 'release' && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' && steps.check_secrets.outputs.HAS_NOTARIZE_SECRETS == 'true' }} + NOTARIZE: ${{ (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.macos_notarize == 'true')) && steps.check_secrets.outputs.HAS_APPLE_SECRET == 'true' && steps.check_secrets.outputs.HAS_NOTARIZE_SECRETS == 'true' }} run: | set -euo pipefail @@ -415,12 +422,12 @@ jobs: MACOS_ARCH="intel" fi BASE_NAME="chiavdf-${RELEASE_TAG}-macos-${MACOS_ARCH}" - if [ "$IS_RELEASE" = "true" ] && [ "$HAS_APPLE_SECRET" != "true" ]; then - echo "Release build requires Apple signing certificate secrets (APPLE_DEV_ID_APP/APPLE_DEV_ID_APP_PASS)." >&2 + if [ "$REQUEST_NOTARIZE" = "true" ] && [ "$HAS_APPLE_SECRET" != "true" ]; then + echo "Notarization requires Apple signing certificate secrets (APPLE_DEV_ID_APP/APPLE_DEV_ID_APP_PASS)." >&2 exit 1 fi - if [ "$IS_RELEASE" = "true" ] && [ "$HAS_NOTARIZE_SECRETS" != "true" ]; then - echo "Release build requires notarization secrets (APPLE_NOTARIZE_USERNAME/APPLE_NOTARIZE_PASSWORD/APPLE_TEAM_ID)." >&2 + if [ "$REQUEST_NOTARIZE" = "true" ] && [ "$HAS_NOTARIZE_SECRETS" != "true" ]; then + echo "Notarization requires secrets (APPLE_NOTARIZE_USERNAME/APPLE_NOTARIZE_PASSWORD/APPLE_TEAM_ID)." >&2 exit 1 fi if [ "$HAS_APPLE_SECRET" = "true" ] || [ "$IS_RELEASE" = "true" ]; then @@ -556,8 +563,8 @@ jobs: fi SIGNING_STATUS="signed + notarized" else - if [ "$IS_RELEASE" = "true" ]; then - echo "Release build reached unexpected state: notarization was not enabled." >&2 + if [ "$REQUEST_NOTARIZE" = "true" ]; then + echo "Build reached unexpected state: notarization was requested but not enabled." >&2 exit 1 fi SIGNING_STATUS="signed (notarization skipped for non-release run)" From 8194d88d910e568f857d21fbe65c88d7403168ab Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 17:50:01 -0800 Subject: [PATCH 17/21] fix(ci): emit homebrew metadata from release workflow Publish Homebrew-friendly macOS asset aliases and include per-arch URLs, hashes, and gmp runtime dependency data in the glue trigger payload. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 40 ++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 79e874b4..ee52ca03 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -98,7 +98,8 @@ jobs: if: startsWith(matrix.os, 'macos') run: | brew ls --versions cmake >/dev/null 2>&1 || brew install cmake - # TODO: Make gmp a prerequisite once brew cask/tap wiring is in place. + # Keep gmp installed for CI builds/tests; Homebrew formulas must also + # declare gmp as a runtime dependency for end-user installs. brew ls --versions gmp >/dev/null 2>&1 || brew install gmp brew ls --versions boost >/dev/null 2>&1 || brew install boost echo "DYLD_FALLBACK_LIBRARY_PATH=$(brew --prefix gmp)/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}" >> "$GITHUB_ENV" @@ -579,6 +580,16 @@ jobs: printf "macOS %s release bundle status: %s\n" "$MACOS_ARCH" "$SIGNING_STATUS" > "dist/macos/${BASE_NAME}.signing-status.txt" shasum -a 256 "dist/macos/${ASSET_NAME}" | awk '{print $1}' > "dist/macos/${ASSET_NAME}.sha256" + if [ "$IS_RELEASE" = "true" ]; then + if [ "$MACOS_ARCH" = "arm64" ]; then + BREW_ARCH="arm64" + else + BREW_ARCH="amd64" + fi + BREW_ASSET_NAME="chiavdf-darwin-${BREW_ARCH}.zip" + cp "dist/macos/${ASSET_NAME}" "dist/macos/${BREW_ASSET_NAME}" + cp "dist/macos/${ASSET_NAME}.sha256" "dist/macos/${BREW_ASSET_NAME}.sha256" + fi - name: Upload macOS brew bundle artifact if: startsWith(matrix.os, 'macos') && matrix.config == 'optimized=1' @@ -721,10 +732,35 @@ jobs: - uses: Chia-Network/actions/github/jwt@main + - name: Build Homebrew release metadata + id: brew_metadata + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: | + set -euo pipefail + BREW_TMP_DIR="${RUNNER_TEMP}/brew-release-assets" + mkdir -p "$BREW_TMP_DIR" + gh release download "$RELEASE_TAG" --dir "$BREW_TMP_DIR" --pattern "chiavdf-darwin-amd64.zip.sha256" --pattern "chiavdf-darwin-arm64.zip.sha256" + AMD64_SHA256="$(awk '{print $1}' "$BREW_TMP_DIR/chiavdf-darwin-amd64.zip.sha256")" + ARM64_SHA256="$(awk '{print $1}' "$BREW_TMP_DIR/chiavdf-darwin-arm64.zip.sha256")" + if ! [[ "$AMD64_SHA256" =~ ^[0-9a-f]{64}$ ]]; then + echo "Invalid amd64 sha256 in release metadata: $AMD64_SHA256" >&2 + exit 1 + fi + if ! [[ "$ARM64_SHA256" =~ ^[0-9a-f]{64}$ ]]; then + echo "Invalid arm64 sha256 in release metadata: $ARM64_SHA256" >&2 + exit 1 + fi + AMD64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/chiavdf-darwin-amd64.zip" + ARM64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/chiavdf-darwin-arm64.zip" + JSON_DATA="$(printf '{"release_version":"%s","runtime_dependencies":["gmp"],"homebrew":{"formula":"chiavdf","runtime_dependencies":["gmp"],"assets":{"darwin-amd64":{"url":"%s","sha256":"%s"},"darwin-arm64":{"url":"%s","sha256":"%s"}}}}' "$RELEASE_TAG" "$AMD64_URL" "$AMD64_SHA256" "$ARM64_URL" "$ARM64_SHA256")" + echo "json_data=${JSON_DATA}" >> "$GITHUB_OUTPUT" + - name: Trigger repo update uses: Chia-Network/actions/github/glue@main with: - json_data: '{"release_version":"${{ github.event.release.tag_name }}"}' + json_data: ${{ steps.brew_metadata.outputs.json_data }} glue_url: ${{ secrets.GLUE_API_URL }} glue_project: "chiavdf" glue_path: "trigger" From 09351dcdf759dc24d09125361b29f2c1741cc088 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 17:58:12 -0800 Subject: [PATCH 18/21] fix(ci): flatten chiavdf homebrew trigger payload Use flat top-level JSON fields for formula inputs so downstream workflow_dispatch wiring can consume values without nested parsing. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index ee52ca03..30249917 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -754,7 +754,7 @@ jobs: fi AMD64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/chiavdf-darwin-amd64.zip" ARM64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/chiavdf-darwin-arm64.zip" - JSON_DATA="$(printf '{"release_version":"%s","runtime_dependencies":["gmp"],"homebrew":{"formula":"chiavdf","runtime_dependencies":["gmp"],"assets":{"darwin-amd64":{"url":"%s","sha256":"%s"},"darwin-arm64":{"url":"%s","sha256":"%s"}}}}' "$RELEASE_TAG" "$AMD64_URL" "$AMD64_SHA256" "$ARM64_URL" "$ARM64_SHA256")" + JSON_DATA="$(printf '{"release_version":"%s","formula":"chiavdf","runtime_dependency":"gmp","mac_amd64_url":"%s","mac_amd64_sha256":"%s","mac_arm64_url":"%s","mac_arm64_sha256":"%s"}' "$RELEASE_TAG" "$AMD64_URL" "$AMD64_SHA256" "$ARM64_URL" "$ARM64_SHA256")" echo "json_data=${JSON_DATA}" >> "$GITHUB_OUTPUT" - name: Trigger repo update From ba269bf7f1da9c1722c65b59c28fa85280f9b895 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 18:03:07 -0800 Subject: [PATCH 19/21] fix(ci): send only release_version to homebrew trigger Pass only release_version in the glue trigger payload since the downstream homebrew workflow derives URLs and hashes from that input. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 30249917..69a1450c 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -754,7 +754,7 @@ jobs: fi AMD64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/chiavdf-darwin-amd64.zip" ARM64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/chiavdf-darwin-arm64.zip" - JSON_DATA="$(printf '{"release_version":"%s","formula":"chiavdf","runtime_dependency":"gmp","mac_amd64_url":"%s","mac_amd64_sha256":"%s","mac_arm64_url":"%s","mac_arm64_sha256":"%s"}' "$RELEASE_TAG" "$AMD64_URL" "$AMD64_SHA256" "$ARM64_URL" "$ARM64_SHA256")" + JSON_DATA="$(printf '{"release_version":"%s"}' "$RELEASE_TAG")" echo "json_data=${JSON_DATA}" >> "$GITHUB_OUTPUT" - name: Trigger repo update From 4b148af40216479f892de07571c28154ab5036a5 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 18:57:47 -0800 Subject: [PATCH 20/21] fix(ci): enforce strict macOS release signing and streamline brew payload Require release builds to have signing and notarization secrets up front so tagged releases fail fast if notarization cannot run, and keep non-release unsigned fallback behavior unchanged. Also send only release_version in the Homebrew trigger metadata. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 31 ++++++++++------------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 69a1450c..33c4d914 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -423,6 +423,16 @@ jobs: MACOS_ARCH="intel" fi BASE_NAME="chiavdf-${RELEASE_TAG}-macos-${MACOS_ARCH}" + if [ "$IS_RELEASE" = "true" ]; then + if [ "$HAS_APPLE_SECRET" != "true" ]; then + echo "Release builds require Apple signing certificate secrets (APPLE_DEV_ID_APP/APPLE_DEV_ID_APP_PASS)." >&2 + exit 1 + fi + if [ "$HAS_NOTARIZE_SECRETS" != "true" ]; then + echo "Release builds require notarization secrets (APPLE_NOTARIZE_USERNAME/APPLE_NOTARIZE_PASSWORD/APPLE_TEAM_ID)." >&2 + exit 1 + fi + fi if [ "$REQUEST_NOTARIZE" = "true" ] && [ "$HAS_APPLE_SECRET" != "true" ]; then echo "Notarization requires Apple signing certificate secrets (APPLE_DEV_ID_APP/APPLE_DEV_ID_APP_PASS)." >&2 exit 1 @@ -571,10 +581,6 @@ jobs: SIGNING_STATUS="signed (notarization skipped for non-release run)" fi else - if [ "$IS_RELEASE" = "true" ]; then - echo "Release build reached unexpected state: bundle was not signed." >&2 - exit 1 - fi (cd dist/macos && ditto -c -k --keepParent "${BASE_NAME}" "${ASSET_NAME}") fi @@ -739,22 +745,7 @@ jobs: RELEASE_TAG: ${{ github.event.release.tag_name }} run: | set -euo pipefail - BREW_TMP_DIR="${RUNNER_TEMP}/brew-release-assets" - mkdir -p "$BREW_TMP_DIR" - gh release download "$RELEASE_TAG" --dir "$BREW_TMP_DIR" --pattern "chiavdf-darwin-amd64.zip.sha256" --pattern "chiavdf-darwin-arm64.zip.sha256" - AMD64_SHA256="$(awk '{print $1}' "$BREW_TMP_DIR/chiavdf-darwin-amd64.zip.sha256")" - ARM64_SHA256="$(awk '{print $1}' "$BREW_TMP_DIR/chiavdf-darwin-arm64.zip.sha256")" - if ! [[ "$AMD64_SHA256" =~ ^[0-9a-f]{64}$ ]]; then - echo "Invalid amd64 sha256 in release metadata: $AMD64_SHA256" >&2 - exit 1 - fi - if ! [[ "$ARM64_SHA256" =~ ^[0-9a-f]{64}$ ]]; then - echo "Invalid arm64 sha256 in release metadata: $ARM64_SHA256" >&2 - exit 1 - fi - AMD64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/chiavdf-darwin-amd64.zip" - ARM64_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/chiavdf-darwin-arm64.zip" - JSON_DATA="$(printf '{"release_version":"%s"}' "$RELEASE_TAG")" + JSON_DATA="$(jq -nc --arg release_version "$RELEASE_TAG" '{release_version:$release_version}')" echo "json_data=${JSON_DATA}" >> "$GITHUB_OUTPUT" - name: Trigger repo update From 8e2e06e8de19358f68e34dfbe6aab1146b73dd27 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Mon, 23 Feb 2026 19:03:56 -0800 Subject: [PATCH 21/21] fix(ci): restore glue trigger release tag payload Revert the glue trigger json_data to use github.event.release.tag_name directly for parity with main. Thanks @cmmarslender for guiding me to the right answer. Co-authored-by: Cursor --- .github/workflows/build-packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 33c4d914..eff58afd 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -751,7 +751,7 @@ jobs: - name: Trigger repo update uses: Chia-Network/actions/github/glue@main with: - json_data: ${{ steps.brew_metadata.outputs.json_data }} + json_data: '{"release_version":"${{ github.event.release.tag_name }}"}' glue_url: ${{ secrets.GLUE_API_URL }} glue_project: "chiavdf" glue_path: "trigger"