macOS Release Signing Requirements for Permanent Identity in no-mistakes
The no‑mistakes project enforces a strict, test‑driven contract requiring all macOS releases to be signed with a permanent Developer ID Application identity (Team ID 9T2J7MNUP9 and bundle identifier com.kunchenguid.no‑mistakes) that can never change after the first signed release.
The kunchenguid/no‑mistakes repository implements rigorous macOS release signing requirements to guarantee binary integrity and user trust. These requirements are encoded as an immutable contract in workflow_release_signing_test.go and documented in AGENTS.md, ensuring that every distributed artifact retains a stable, verifiable identity across all future updates.
Permanent Identity Specifications
The cornerstone of the macOS release process is the permanent signing identity. According to lines 27‑29 of workflow_release_signing_test.go, the following values are immutable once the first signed release is published:
- Bundle Identifier: Must be exactly
com.kunchenguid.no‑mistakes - Team ID: Must be
9T2J7MNUP9
These values are hard‑coding requirements in the test suite TestReleaseWorkflowFailsClosedOnBadSignature. Any deviation from this identity causes the release workflow to fail closed, preventing the distribution of improperly signed binaries.
Architecture and Runner Requirements
Official macOS releases must target both modern architectures. The test constant signingDarwinArches (line 35) mandates that the workflow produce separate signed binaries for amd64 and arm64.
Furthermore, cross‑compilation is prohibited. The validation logic runsOnMacOS() (lines 22‑23) asserts that each darwin build job must execute on a macOS runner (runs‑on: macos‑*), ensuring that codesign operates within a native Keychain environment.
The Signing Workflow
The signing process follows a strict protocol defined in .github/workflows/release.yml and enforced by the test suite. The workflow must execute four phases in exact order: sign → verify → archive → upload.
Required Codesign Flags
Within each darwin job, the signStepIndex implementation (lines 41‑44) validates that the codesign invocation includes the following flags:
codesign --sign "$CSC_LINK" \
--options runtime \
--identifier com.kunchenguid.no-mistakes \
--timestamp "$TIMESTAMP" \
no-mistakes-darwin-amd64.tar.gz
The --options runtime flag is mandatory; its absence causes immediate rejection during verification. The timestamp must be a live value generated at runtime, not a static string.
Step Ordering Enforcement
The test TestReleaseWorkflowSignsBeforeArchiveAndChecksum (lines 71‑86) guarantees that within each darwin job, steps appear in this sequence:
- Sign the binary with the permanent identity
- Verify the signature integrity
- Archive the signed binary
- Upload the release asset
Additionally, the checksums job must depend on each darwin build job (lines 89‑103) so that published checksums cover the cryptographically signed archives, not unsigned intermediates.
Verification and Security Controls
After signing, the workflow must verify the binary using codesign --verify --strict. The verifyStepIndex logic in TestReleaseWorkflowFailsClosedOnBadSignature (lines 40‑60) asserts the following cryptographic properties:
- Team ID matches
9T2J7MNUP9 - Identifier matches
com.kunchenguid.no‑mistakes - Anchor is
apple generic - Subject.OU is present and valid
- Rejection of ad‑hoc signatures and content‑hash (
cdhash) requirements - Presence of the
runtimeflag
The timestamp handling logic (lines 61‑64) mandates that the signing script generate a TIMESTAMP variable; the workflow must fail if this value is empty or equal to the string "none".
Secrets Scoping and Keychain Management
To prevent secret leakage, TestReleaseWorkflowScopesSigningSecretsToDarwin (lines 28‑38) restricts signing credentials to the release-signing environment only. The workflow may reference only two secrets:
CSC_LINK(the Developer ID certificate)CSC_KEY_PASSWORD(the certificate password)
No other job may access these secrets. The temporary keychain password must be generated at runtime using openssl rand (line 40), and TestReleaseWorkflowCleansUpKeychainAlways (lines 78‑85) requires a cleanup step that deletes the temporary keychain with if: always() to guarantee execution regardless of job success or failure.
Phase 1 Scope Limitations
The current release workflow explicitly forbids additional Apple distribution features. TestReleaseWorkflowStaysPhase1NoNotarization (lines 62‑68) asserts that the workflow must not contain:
- Notarization steps
- Stapling operations
pkgbuildinvocations- Universal binary creation
- Homebrew tooling
Only the signing and verification steps described above are permitted in this phase.
Configuration Example
Below is a representative excerpt from .github/workflows/release.yml illustrating the required macOS signing job structure:
jobs:
build-darwin-amd64:
runs-on: macos-13
environment: release-signing
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
steps:
- name: Build binary
run: go build -o no-mistakes-darwin-amd64
- name: Create temporary keychain
run: |
export KEYCHAIN_PASSWORD=$(openssl rand -hex 12)
security create-keychain -p "$KEYCHAIN_PASSWORD" temp.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" temp.keychain
security import <(echo "$CSC_LINK" | base64 -d) -k temp.keychain -P "$CSC_KEY_PASSWORD" -T /usr/bin/codesign
- name: Sign binary
run: |
export TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
codesign --sign "Developer ID Application" \
--options runtime \
--identifier com.kunchenguid.no-mistakes \
--timestamp "$TIMESTAMP" \
--keychain temp.keychain \
no-mistakes-darwin-amd64
- name: Verify signature
run: |
codesign --verify --strict --verbose=2 no-mistakes-darwin-amd64
codesign -dvv no-mistakes-darwin-amd64 | grep -E "Identifier|TeamIdentifier"
- name: Archive signed binary
run: tar czf no-mistakes-${GIT_TAG}-darwin-amd64.tar.gz no-mistakes-darwin-amd64
- name: Upload release asset
run: gh release upload "${GIT_TAG}" no-mistakes-${GIT_TAG}-darwin-amd64.tar.gz
- name: Delete temporary keychain
if: always()
run: security delete-keychain temp.keychain
Summary
- Permanent identity is immutable: Team ID
9T2J7MNUP9and identifiercom.kunchenguid.no‑mistakesare hard‑coded inworkflow_release_signing_test.goand cannot change after the first release. - Dual architecture support is required for both
amd64andarm64, built exclusively on macOS runners. - Strict step ordering (sign → verify → archive → upload) is enforced by
TestReleaseWorkflowSignsBeforeArchiveAndChecksum. - Verification must confirm the Team ID, identifier,
runtimeflag, and reject ad‑hoc signatures. - Secrets isolation restricts
CSC_LINKandCSC_KEY_PASSWORDto therelease-signingenvironment only. - Phase 1 restrictions prohibit notarization, stapling, or universal binaries until explicitly permitted by future contract updates.
Frequently Asked Questions
What constitutes the permanent identity for no‑mistakes macOS releases?
The permanent identity consists of a Developer ID Application certificate issued to Team ID 9T2J7MNUP9 and a bundle identifier set to com.kunchenguid.no‑mistakes. These values are hard‑coded assertions in workflow_release_signing_test.go (lines 27‑29) and must remain constant for all future releases to ensure cryptographic continuity.
Why must macOS builds run on macOS runners instead of using cross‑compilation?
The runsOnMacOS() validation (lines 22‑23) requires native macOS execution because the codesign tool relies on the macOS Keychain infrastructure to access signing certificates and validate code signatures. Cross‑compiled binaries cannot be properly signed or verified without access to this native security layer.
What happens if the signing timestamp is missing or set to "none"?
The test TestReleaseWorkflowFailsClosedOnBadSignature (lines 61‑64) asserts that the workflow must generate a valid timestamp at runtime. If the TIMESTAMP variable is empty or equals the literal string "none", the workflow fails immediately, preventing the release of binaries that lack secure timestamping.
Which secrets are required for the signing process and how are they scoped?
The workflow requires exactly two secrets: CSC_LINK (the base64‑encoded Developer ID certificate) and CSC_KEY_PASSWORD (the certificate passphrase). According to TestReleaseWorkflowScopesSigningSecretsToDarwin (lines 28‑38), these secrets are scoped exclusively to jobs running in the release-signing environment; no other workflow jobs may reference them, ensuring minimal attack surface for credential exposure.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →