Distribution nodes · Dedicated signer · Profile rotation · Keychain scope · Decision matrix
Mobile and platform leads running a multi-node remote Mac mesh often hit “Archive signs here, CI fails provisioning there”: rotation windows drift, distribution certs land on shared disks, or every runner imports its own .p12 and Team context diverges. This article compares dedicated signer, per-runner identities, and controlled distribution, spells profile-to-bundle mapping with rotation and idempotency rules, adds a six-step runbook, and closes with a size × compliance × release cadence matrix. Cross-links to the Golden Image drift checklist, shared build pool runners, and OIDC credential vaulting keep signing context aligned with toolchain batches.
You followed the Golden Image checklist yet still see errSecInternalComponent or provisioning mismatches across the mesh. Root cause is usually that signing context never became first-class pipeline metadata: profile UUID, Team ID, certificate fingerprints, and Keychain scope must be as auditable as IMAGE_ID. With a shared build pool, letting runners import arbitrary .p12 bundles breaks compliance and triage at the same time.
Rotation windows: After Apple Developer updates profiles, stale UUIDs linger on some nodes and failures look random; pin explicit manifest versions instead of “download latest.”
Distribution cert sprawl: One .p12 copied to many mesh hosts means one revocation halts everything and audits cannot answer who imported the private key where.
Keychain scope drift: login vs System keychains plus inconsistent unlock policy for the CI user make headless signing intermittently miss identities.
Multi–App ID mapping errors: Extensions use different provisioning than the host app but PROVISIONING_PROFILE_SPECIFIER is hard-coded once in xcodebuild args.
Profiles mixed with build caches: Storing profiles beside DerivedData in “safe to wipe” trees makes cleaners delete them overnight.
Add these five to on-call ordering as “signing layer before compiler layer” to cut wasted retries. Human-in-the-loop latency on handoffs magnifies waits—pair with the SSH vs VNC checklist to decide who may click Keychain prompts on a signer host.
No topology wins everywhere—match revocation blast radius, compliance evidence, and mesh elasticity. Dedicated signers minimize revocation surface but queue; per-runner keys scale concurrency but explode audit cost; controlled distribution sits between them and needs manifest plus read-only mounts. Same lesson as OIDC secret vaulting: private key material should follow shortest life and smallest exposure.
| Dimension | Dedicated signer | Per-runner identity | Controlled distribution |
|---|---|---|---|
| Revocation radius | Smallest; rotations stay bounded | Largest; per-host tracing | Medium; manifest versions |
| Queue & mesh elasticity | Bottleneck risk; booking or sidecar export | High concurrency | Medium-high; parallel profile fetch |
| Compliance audit | Easiest; access and export logged | Hardest; keys scattered | Medium; prove mounts are immutable |
| Golden Image coupling | Signer can track its own batch | Certs drift from image IDs | Profile rev belongs beside image metadata |
| Anti-patterns | Using signer as generic compile host | Committing .p12 to artifact stores | “Always fetch latest profile” jobs |
Governance is healthy when one revocation maps in minutes to affected nodes and pipelines—not when “it usually builds.”
If Archives and PR builds share the mesh, bill and lease signing separately from compile queues; with pool seat locks, avoid holding compile locks while waiting for someone to approve Keychain prompts.
Run these alongside the Golden Image six steps: images own toolchains, this article owns signing artifacts and Keychain boundaries. Each step needs a ticket ID; with pool leases, signer seat acquisition must stay off the compile queue.
Freeze the profile manifest: Store profiles.json (UUID, filename, expiry, Team ID) in git or a guarded bucket; CI gates must match node mounts.
Declare topology in README: dedicated vs distributed vs per-runner plus the hostnames allowed to hold private keys.
Keychain and unlock policy: carve a CI keychain partition and document security unlock-keychain windows plus failure fallback.
Gate every .p12 export: dual control plus ticket numbers—no “temporary export to Desktop.”
Extend probes: beyond toolchain fingerprints, hash security find-identity -v -p codesigning into log indexes.
Rotate in staging: rehearse the seven-day pre-expiry window with parallel UUIDs and ordered rollback.
export PROFILE_MANIFEST_SHA="$(shasum profiles.json | awk '{print $1}')"
export SIGNING_SUMMARY="$(security find-identity -v -p codesigning | shasum | awk '{print $1}')"
node scripts/assert-signing-context.mjs \
--expect-manifest "${PROFILE_MANIFEST_SHA}" \
--expect-signing "${SIGNING_SUMMARY}" \
--region "${RUNNER_REGION}"
Note: Probe output belongs in log indexes only—do not ship private-key fingerprints in public artifact metadata; external SBOMs can use last-six serial digits or internal aliases.
The classic false positive is host app profile updated while extensions still reference the old UUID. Triage order: compare embedded.mobileprovision with build args, then compare Keychain identity summaries, then Xcode project settings. When chaining with the observable task chain post, include profile_manifest_sha in handoff envelopes.
Evidence triad: Team, Authority, and Sealed Resources from codesign -dvvv.
Manifest diff: Does failing vs passing node share the same profiles.json hash?
Unlock window: Did the first unattended signing fall outside the allowed unlock interval?
Per-target mapping: Each target’s CODE_SIGN_STYLE pairs with its specifier.
Export pipelines: Archive vs ad-hoc must not reuse the wrong profile directory.
Emit idempotency: queue completion carries manifest version to prevent double-sign downstream.
Warning: Do not mix automatic signing with explicit profile file paths during parallel windows—it surfaces as sporadic per-target failures on the mesh.
Three planning bands from cross-region iOS practice—replace with your own telemetry and keep the sample source for audits.
| Team size | Compliance | Release cadence | First stable choice |
|---|---|---|---|
| Small | Standard | Weekly+ | Dedicated signer + explicit manifest; ban shared .p12 |
| Mid | Standard | Daily+ | Controlled distribution + read-only mounts + automated rotation |
| Platform | High | Continuous | HSM-class sidecar + full audit index |
| Multi-vendor | Medium | Irregular | Isolated runner pools + per-project profile prefixes |
Laptops moonlighting as signers inherit sleep, OS updates, and unaudited Keychain prompts; on-prem Mac fleets drag procurement and multi-site sync. Contract-backed remote Mac nodes fit the mesh “signing gate” role better.
Anti-pattern: Treating intermittent successful codesign as proof profiles are fine—force manifest hashes in probes.
Mesh plus auditable signing rarely survives informal policy alone, and borrowed laptops cannot prove private keys stayed inside controlled zones. For reproducible signing and stable gates, VpsMesh Mac Mini cloud rental is usually the better fit: pick region and SKU, dedicate nodes, and separate signer contracts from compile runners so mesh policy becomes enforceable terms instead of personal habit.
Pin profile version and expiry in the pipeline gate; use explicit filenames during parallel UUID windows; align idempotency fields with the shared build pool runners post. For isolated signer nodes see the order page.
Start with Golden Image and drift to freeze toolchain batches, then return here for certificates and profile maps. Compare pricing with the three-year TCO article.
Connectivity lives in the help center; relay baselines in the SSH vs VNC checklist; when profiles misbehave, re-check section three probes and manifest hashes.