# 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](https://signpath.org) 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](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 1. **Source of truth.** All code lives in this public repository. Releases are cut from a specific tagged commit on the default branch. 2. **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: ```powershell .\tools\build_release.ps1 ``` This packages the app with PyInstaller into `dist/…/ZD Ultimate Legend.exe` plus a distributable ZIP (and an installer `.exe` when Inno Setup is present). 3. **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: source tests + CVE audit → build → sign → package the final distributables → hash + attest the final bytes → draft release → smoke-test the final artifacts → 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: source tests + CVE audit → build + package → hash + attest → draft release → smoke-test the final artifacts → publish. 4. **Verify the build.** The unit suite and dependency audit gate the source before packaging. After the workflow creates a draft release, the release smoke test (`.\tools\smoke_release.ps1`) exercises the exact final artifacts before a maintainer publishes them. 5. **Publish hashes.** Each release publishes `SHA256SUMS.txt` computed 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](../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: 1. Right-click `ZD Ultimate Legend.exe` → **Properties → Digital Signatures** and confirm the signer is the LegendCTL project certificate. 2. Independently, check the downloaded artifact's hash against the published `SHA256SUMS.txt`: ```powershell Get-FileHash ".\ZDUltimateLegend-v-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: ```powershell gh attestation verify .\ZDUltimateLegend-v-Setup.exe --repo EvilHumphrey/LegendCTL gh attestation verify .\ZDUltimateLegend-v-windows.zip --repo EvilHumphrey/LegendCTL ``` See [install-windows-and-smartscreen.md](install-windows-and-smartscreen.md) for the full verification guide (including offline verification), and the README's ["First-run SmartScreen, and verifying your download"](../README.md#first-run-smartscreen-and-verifying-your-download) section for what to expect from SmartScreen and antivirus in the meantime.