Code-signing policy
This document describes how LegendCTL release binaries are built and signed. It is also a prerequisite for LegendCTL’s application to the SignPath Foundation free code-signing program for open-source software.
Summary
- Since v2.5.0, official release assets are built by GitHub Actions CI
from the release tag and ship with build-provenance attestations you can
verify today with
gh attestation verify(see install-windows-and-smartscreen.md). Attestation proves which commit and workflow built the exact bytes; it is not AuthentiCode code-signing. - Release executables and installers are (will be) code-signed via the SignPath Foundation, using a certificate issued to this project.
- We sign only our own binaries, built from this repository’s source. No third-party, vendored, or externally supplied executable is ever signed.
- Signing (once live) happens before the final distributables are packaged, hashed, and attested — so published hashes and attestations always describe the exact bytes users download. The build itself is a repeatable, documented build from a tagged commit.
Build & release process
- Source of truth. All code lives in this public repository. Releases are cut from a specific tagged commit on the default branch.
- Build from source. There are two build lanes:
- Official release assets (v2.5.0 and later) are built by GitHub Actions
CI (
.github/workflows/release-build.yml) when the release tag is pushed: the workflow runs the full unit suite and a dependency CVE audit first, then packages the app with PyInstaller (plus the Inno Setup installer) and generates a build-provenance attestation for each distributed binary. - Source builds (and releases before v2.5.0) use the project’s own local
build script:
.\tools\build_release.ps1This packages the app with PyInstaller into
dist/…/ZD Ultimate Legend.exeplus a distributable ZIP (and an installer.exewhen Inno Setup is present).
- Official release assets (v2.5.0 and later) are built by GitHub Actions
CI (
- Sign (future — once SignPath signing is live). The built executable and installer inputs are submitted to SignPath for signing before the final distributables are packaged, because AuthentiCode signing changes a binary’s bytes (and a signed inner executable changes the ZIP/installer bytes). The signed-release flow is: build → sign → package the final distributables → smoke-test the final artifacts → hash + attest the final bytes → publish. Only artifacts produced by step 2 from the tagged commit are submitted. Until signing is live this step is skipped, and the flow is: build → smoke-test → hash + attest → publish.
- Verify the build. The release smoke test (
.\tools\smoke_release.ps1) and the unit suite are run against the final artifacts before a build is considered releasable. - Publish hashes. Each release publishes
SHA256SUMS.txtcomputed over the final published bytes (after signing, once signing is live), so users can verify integrity independently of the signature — hashes and attestations always describe exactly what users download.
Signing scope
We sign only:
- executables and installers built from this repository, for an official tagged release.
We do not sign:
- builds from forks or untrusted branches;
- third-party binaries or dependencies (each dependency keeps its own upstream signature and license — see NOTICE);
- anything a contributor sends pre-built. Pull requests contribute source, which is reviewed and then built by the maintainer — never a pre-compiled binary.
Team & roles
LegendCTL is currently a solo-maintainer project. For SignPath’s role model, the single maintainer (copyright holder EvilHumphrey) holds all three roles:
| Role | Responsibility | Holder |
|---|---|---|
| Author | Requests the signed build | solo maintainer |
| Reviewer | Reviews the change being signed | solo maintainer |
| Approver | Approves the signing request | solo maintainer |
If additional maintainers join, these roles will be separated (a different person approving the signing than authoring it) and this document updated.
Account security
- The maintainer’s source-host account and the SignPath account both have multi-factor authentication (MFA) enabled.
- The signing certificate’s private key is held by SignPath; the project never takes custody of the private key.
- Signing requests are approved manually for each release — signing is not automated to fire on every commit or every CI run.
How users verify a signed release
Once signing is live, you can confirm a downloaded build is the signed release:
- Right-click
ZD Ultimate Legend.exe→ Properties → Digital Signatures and confirm the signer is the LegendCTL project certificate. - Independently, check the downloaded artifact’s hash against the published
SHA256SUMS.txt:Get-FileHash ".\ZDUltimateLegend-v<version>-windows.zip" -Algorithm SHA256
Until signing is in place, releases are unsigned — but from v2.5.0 they are not unverifiable: each release’s binaries carry CI build-provenance attestations you can check with the GitHub CLI:
gh attestation verify .\ZDUltimateLegend-v<version>-Setup.exe --repo EvilHumphrey/LegendCTL
gh attestation verify .\ZDUltimateLegend-v<version>-windows.zip --repo EvilHumphrey/LegendCTL
See install-windows-and-smartscreen.md for the full verification guide (including offline verification), and the README’s “Distribution safety” section for what to expect from SmartScreen and antivirus in the meantime.