Practical traps hit while getting the Aposemati apps onto real devices. Each one presents as something other than what it is.

The WWDR G3 intermediate is missing

Symptom: zero valid identities while the certificate is plainly there

security find-identity -v -p codesigning
#   0 valid identities found

and codesign fails with:

errSecInternalComponent
unable to build chain to self-signed root

Cause: a freshly issued Apple Development certificate is signed by the WWDR G3 intermediate. The machine had G5 and an expired legacy 2023 one, so the chain could not be built — and the tooling reports that as “no valid identities”, not as “missing intermediate”.

Fix: download and install the intermediate.

curl -O https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer
open AppleWWDRCAG3.cer     # or: security import AppleWWDRCAG3.cer
security find-identity -v -p codesigning   # now non-zero

The general lesson

find-identity reporting 0 does not mean the certificate is absent. It means the chain does not validate. Check the intermediates at https://www.apple.com/certificateauthority/ before re-issuing anything.

Xcode must register a device once, by hand

CLI builds with -allowProvisioningUpdates will not provision a new device. Open the project in Xcode and let it register the device once; after that the CLI path works.

Related, from the same session: free-account provisioning profiles expire in 7 days and cap at 3 apps. The paid Apple Developer Program gives 1-year profiles — a second reason to enrol beyond the Phase 7 system-extension entitlement and notarization.

A sandboxed app needs a bookmark, not a path

A stored path does not survive relaunch

A sandboxed macOS app holding a user-chosen folder must store a security-scoped bookmark, not the path string. Entitlement: com.apple.security.files.bookmarks.app-scope.

Two follow-on traps found by review:

  • Check the return value of startAccessingSecurityScopedResource(). Setting root and isCustom regardless means the UI shows a folder the app cannot write to, and the user learns about it once per photo instead of once.
  • Restoring a bookmark for an ejected volume should not silently discard the folder and permanently delete the bookmark.

rtk strips comment lines from file dumps

A false alarm worth recognising

A cat of Package.swift through rtk showed no // swift-tools-version: directive, which reads as a broken manifest. The directive was there — rtk strips comment lines from file dumps.

Cross-check with swift package tools-version (reported 6.2.0) or rtk proxy cat <file> before believing a missing comment-line directive. The same investigation also chased Target Platform: arm64e-apple-macos14.0 in test output as a platform-setting failure; it is swift-testing’s runner platform, and the built object carried minos 26.0.

Smaller notes

  • XcodeGen, and the .xcodeproj is not committed. Both inputs (project.yml + sources) are committed and a from-scratch xcodegen generate + build was verified byte-identical from an out-of-tree copy. Defensible, and it keeps project-file churn out of diffs.
  • swift test --filter <NoSuchThing> exits 0 and reports “0 tests passed”. Any automation must assert the expected test count, never just the exit code. This became a standing process rule for the whole build.
  • Info.plist for a Bonjour peer needs NSLocalNetworkUsageDescription and NSBonjourServices (here exactly [_aposemati._tcp]) on both targets.