Skip to content

docs: document the macOS code-signing identity convention#52

Merged
rianjs merged 4 commits into
mainfrom
docs/51-macos-codesign-convention
Jun 3, 2026
Merged

docs: document the macOS code-signing identity convention#52
rianjs merged 4 commits into
mainfrom
docs/51-macos-codesign-convention

Conversation

@rianjs

@rianjs rianjs commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Documents the macOS code-signing convention so every current and future keychain-backed CLI inherits it (part of the stable-code-signing initiative; shared infra lands in open-cli-collective/.github).

Changes

  • distribution.md §2A "macOS code-signing identity" (new): the ad-hoc/cdhash re-prompt rationale; the org.open-cli-collective.<binary> identifier invariant; the centralized goreleaser hooks.post snippet (one byte-identical line per darwin build, using the absolute $CODESIGN_DARWIN_SCRIPT); the darwin-gate check-signature acceptance gate (parse only the designated => line; pin certificate leaf, no cdhash); per-caller self-gating; the one-time final re-prompt.
  • distribution.md §6: the four MACOS_CERT_* secrets + the explicit four-line secrets: pass-through rule (never inherit).
  • release.md §4 and ci.md §4: cross-references to §2A.

Docs-only. make check passes (tidy/lint/test).

Closes #51

Add distribution.md §2A (macOS code-signing identity): the ad-hoc/cdhash
re-prompt rationale, the org.open-cli-collective.<binary> identifier invariant,
the centralized goreleaser hooks.post snippet, and the check-signature
acceptance gate. Extend §6 with the four MACOS_CERT_* secrets and the explicit
four-line secrets: pass-through rule (never inherit). Cross-reference from
release.md §4 and ci.md §4.

Closes #51
@rianjs

rianjs commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Findings:

  • Nit: docs/distribution.md:145 still says the cask removes quarantine for “the unsigned binary.” With §2A now making release-time signing the standard, that should become “non-notarized binary” or “Homebrew-installed binary” to avoid contradicting the new convention. The xattr hook still makes sense; only the adjective is stale.

No blockers, majors, or minors. The new §2A matches the approved architecture: stable org.open-cli-collective.<binary> identifier, centralized absolute CODESIGN_DARWIN_SCRIPT hook, self-gated signing/enforcement, DR-line-only parsing, explicit four-secret pass-through, and one final prompt expectation.

§2A now makes release-time signing the standard, so the cask's quarantine
removal applies to the non-notarized (not unsigned) binary.

@monit-reviewer monit-reviewer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR Review

Reviewed commit: 0e27ad5

Approved with 2 non-blocking suggestions below. Address at your discretion.

Summary

Reviewer Findings
harness-engineering:harness-enforcement-reviewer 1
harness-engineering:harness-knowledge-reviewer 1
harness-engineering:harness-enforcement-reviewer (1 findings)

💡 Suggestion - docs/distribution.md:145

Markdown list-item continuation line starting with 'notarized — Gatekeeper still quarantines downloads' has no leading indentation. CommonMark requires continuation lines to be indented to at least the bullet's content column (2 spaces here); GitHub's renderer will break this into a separate paragraph, producing a loose list and a floating sentence fragment. Add 2-space indentation to the continuation line.

harness-engineering:harness-knowledge-reviewer (1 findings)

💡 Suggestion - docs/distribution.md:93

The §2A invariants block states the cert is 'generated once and reused forever' but does not address cert expiration. Self-signed certs have a finite validity period; expiry simultaneously breaks every user's 'Always Allow' Keychain grant for every signed tool — a larger blast radius than the per-upgrade ad-hoc rehash problem §2A is solving. The invariants section should note the recommended cert lifetime (e.g. 10–25 years) and the migration path (re-release all tools, expect one re-prompt per user) so operators can plan for eventual rotation.

4 info-level observations excluded. Run with --verbose to include.

1 PR discussion thread considered.


Completed in 4m 19s | $0.75 | sonnet | daemon 0.2.121 | Glorfindel
Field Value
Model sonnet
Reviewers hybrid-synthesis, documentation:docs-reviewer, harness-engineering:harness-architecture-reviewer, harness-engineering:harness-enforcement-reviewer, harness-engineering:harness-knowledge-reviewer
Engine claude · sonnet
Reviewed by pr-review-daemon · monit-pr-reviewer
Duration 4m 19s wall · 8m 09s compute (Reviewers: 3m 20s · Synthesis: 56s)
Cost $0.75
Tokens 154.1k in / 31.9k out
Turns 9

Per-workstream usage

Workstream Model In Out Cache read Cache create Cost
hybrid-synthesis sonnet 30.9k 3.6k 0 30.9k (1h) $0.17
documentation:docs-reviewer haiku 5.8k 7.7k 0 5.8k (1h) $0.05
harness-engineering:harness-architecture-reviewer sonnet 46.2k 3.2k 33.9k 12.3k (1h) $0.11
harness-engineering:harness-enforcement-reviewer sonnet 38.6k 11.6k 17.8k 20.8k (1h) $0.26
harness-engineering:harness-knowledge-reviewer sonnet 32.7k 5.8k 17.8k 14.9k (1h) $0.15

Re-reviews only run when @monit-reviewer is re-requested as a reviewer — push as many commits as you need, then re-request when ready. PRs targeting branches other than main, master are skipped, even when @monit-reviewer is re-requested.

Comment thread docs/distribution.md Outdated
differ).
- A **cask, not a formula** — we ship a prebuilt binary, not a source build. The
cask also handles Gatekeeper quarantine removal for the unsigned binary. The
cask also handles Gatekeeper quarantine removal for the non-notarized binary (signed per §2A, but not

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Low (harness-engineering:harness-enforcement-reviewer): Markdown list-item continuation line starting with 'notarized — Gatekeeper still quarantines downloads' has no leading indentation. CommonMark requires continuation lines to be indented to at least the bullet's content column (2 spaces here); GitHub's renderer will break this into a separate paragraph, producing a loose list and a floating sentence fragment. Add 2-space indentation to the continuation line.

Reply to this thread when addressed.

Comment thread docs/distribution.md
— no `cdhash` — so the grant survives rebuilds **as long as the cert and identifier
never change.** No Apple Developer account, no notarization: Homebrew binaries aren't
quarantined and we establish a local identity, not Gatekeeper trust — so `codesign`
MUST omit `--timestamp` and `--options runtime`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Low (harness-engineering:harness-knowledge-reviewer): The §2A invariants block states the cert is 'generated once and reused forever' but does not address cert expiration. Self-signed certs have a finite validity period; expiry simultaneously breaks every user's 'Always Allow' Keychain grant for every signed tool — a larger blast radius than the per-upgrade ad-hoc rehash problem §2A is solving. The invariants section should note the recommended cert lifetime (e.g. 10–25 years) and the migration path (re-release all tools, expect one re-prompt per user) so operators can plan for eventual rotation.

Reply to this thread when addressed.

rianjs added 2 commits June 3, 2026 15:16
Document that the DR pins the leaf hash (not expiry) so an expired cert doesn't
break existing grants, but new builds can't be signed with it. Reflow the §3
cask continuation lines to keep the CommonMark bullet indentation.
State the cask removes any quarantine metadata for the non-notarized binary,
without the Gatekeeper-quarantines claim that read as contradicting §2A.
@rianjs

rianjs commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Codex final pass (9e) — post-daemon. STATUS: blockers=0 majors=0 minors=1; the minor (§3/§2A quarantine wording) is fixed in the follow-up commit.

Findings:

  • Minor: distribution.md now says in §2A that “Homebrew binaries aren't quarantined,” while §3 says “Gatekeeper still quarantines downloads.” That is easy to read as contradictory. The architecture intent is: signing is for Keychain identity, not Gatekeeper; the cask/postflight removes quarantine for Homebrew installs, so notarization/runtime options are unnecessary. Suggested §3 wording: “the cask also removes quarantine metadata for the non-notarized binary if present (signed per §2A, but not notarized).”

The cert-expiry invariant is accurate and consistent with the agreed design: DR continuity depends on the pinned leaf hash, while signing new builds depends on the cert still being valid.

@rianjs rianjs merged commit 7d7e445 into main Jun 3, 2026
4 checks passed
@rianjs rianjs deleted the docs/51-macos-codesign-convention branch June 3, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document the macOS code-signing identity convention (distribution.md §2A + §6)

2 participants