Commit df69393f7e

df69393f7e556dccfacfb05378fcce5c9ffc125b

parent: 017785c4ce

Verified · cmc

cmc <hello@cleberg.net> · 2026-08-22 08:02 UTC

Do not touch CloudKit from a build without entitlements

The app aborted before its first screen on any unsigned build, which is what CI
produces: xcodebuild ... CODE_SIGNING_ALLOWED=NO embeds no entitlements, so
CKContainer.default() could not resolve the iCloud container from them. It does
not return an error in that case — it raises an Objective-C exception from
inside a dispatch_once, which Swift cannot catch, so the existing do/catch never
had a chance and the process took SIGABRT. Every AccessibilityAuditTests case
then failed as 'application is not running'.

An explicit CKContainer(identifier:) crashes the same way; CloudKit validates
against entitlements either way. So the fix is to not ask. The App Group is
declared in the same entitlements file and stripped by the same mechanism, but
asking for its container returns nil rather than raising, which answers the
question CloudKit will not: does this process have entitlements at all.

Signed builds are unaffected — the container resolves, the guard passes, owner
resolution runs as before. This is why the audit job has failed since
2026-07-25; the run on krz-rebrand shows the same crash three weeks before this
branch existed.

Layout: unified · split

DomainDig/OwnerAccess.swift +21 −2
@@ -14,10 +14,29 @@ enum OwnerAccess {
1414
1515 static var isConfigured: Bool { !ownerUserRecordID.isEmpty }
1616
17 /// Whether this build actually carries its entitlements.
18 ///
19 /// Touching CloudKit without the iCloud container entitlement does not
20 /// return an error — it raises an Objective-C exception from inside a
21 /// `dispatch_once`, which Swift cannot catch, so the process aborts before
22 /// the first screen draws. That is what any unsigned build does, including
23 /// CI: `xcodebuild ... CODE_SIGNING_ALLOWED=NO` embeds no entitlements.
24 ///
25 /// The App Group is declared in the same entitlements file and is stripped
26 /// by the same mechanism, but asking for its container returns nil rather
27 /// than raising. So it answers the question CloudKit will not: does this
28 /// process have its entitlements at all?
29 private static var hasEntitlements: Bool {
30 FileManager.default.containerURL(
31 forSecurityApplicationGroupIdentifier: DomainDigWidgetStore.appGroupID
32 ) != nil
33 }
34
1735 /// The current iCloud user's record name for this app's container, or nil if
18 /// it is unavailable (not signed into iCloud, restricted, or offline before
19 /// the first fetch).
36 /// it is unavailable (not signed into iCloud, restricted, offline before the
37 /// first fetch, or running from a build without entitlements).
2038 static func currentUserRecordName() async -> String? {
39 guard hasEntitlements else { return nil }
2140 do {
2241 return try await CKContainer.default().userRecordID().recordName
2342 } catch {