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:

  1. Sign the binary with the permanent identity
  2. Verify the signature integrity
  3. Archive the signed binary
  4. 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 runtime flag

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
  • pkgbuild invocations
  • 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 9T2J7MNUP9 and identifier com.kunchenguid.no‑mistakes are hard‑coded in workflow_release_signing_test.go and cannot change after the first release.
  • Dual architecture support is required for both amd64 and arm64, built exclusively on macOS runners.
  • Strict step ordering (sign → verify → archive → upload) is enforced by TestReleaseWorkflowSignsBeforeArchiveAndChecksum.
  • Verification must confirm the Team ID, identifier, runtime flag, and reject ad‑hoc signatures.
  • Secrets isolation restricts CSC_LINK and CSC_KEY_PASSWORD to the release-signing environment 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →