# macOS Release Signing Requirements for Permanent Identity in no-mistakes

> Discover macOS release signing requirements for permanent identity in no-mistakes. Learn about the strict, test-driven contract for unchanging, permanent Developer ID Application signing.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: getting-started
- Published: 2026-07-18

---

**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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/.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:

```bash
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`](https://github.com/kunchenguid/no-mistakes/blob/main/.github/workflows/release.yml) illustrating the required macOS signing job structure:

```yaml
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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/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.